Search code examples
bashgrepwaitexit-code

How do I get the exit code of a PID outside of the parent shell?


Background

I am trying to get the exit code of a command launched via x-terminal-emulator -e. Using this command, it simply opens a new terminal window and passes it sh -c with the command that you want to be executed within the new terminal window. I then use a combination of ps ax, grep, xargs & cut to get the PID of the running process.

👨🏼‍💻tester.sh

#!/bin/bash

execute() {

    local -r CMDS="$1"
    local exitCode=0
    local cmdsPID=""

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # Execute a command within a 
    # a new 'x-terminal-emulator' window.

    x-terminal-emulator -e "$CMDS" &> /dev/null

    # Get the PID of the process spawned by
    # 'x-terminal-emulator'.

    cmdsPID="$(ps ax | grep "sh -c $CMDS" | xargs | cut -d ' ' -f 1)"

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # Wait for the commands to no longer be executing
    # in the background, and then get their exit code.

    wait "$cmdsPID" &> /dev/null
    exitCode=$?

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # Print output based on what happened.

    echo "$exitCode"    

}

execute "apt update && apt upgrade -y && apt full-upgrade -y && apt autoremove -y && apt clean"

⚠️ Error

However, when I utilize the command wait to wait for the PID to finish running so that I can get the exit code of the command, wait returns:

pid #### is not a child of this shell

Is there a way where I can both wait for the PID to finish, then grab its exit code from a process not spawned under the parent shell?


Solution

  • After a bit of trial and error and some research, I have come up with a solution.

    My solution involves the following:

    1. The use of redirecting $? output from within the new terminal window that was spawned from x-terminal-emulator to a temporary file utilizing mktemp.
    2. Then, utilizing the until conditional structure to wait until that temporary file is not empty.
    3. Once the temporary file is not empty, I can then cat the contents of the file and store it within a variable. Then, echo the results.

    👨🏼‍💻tester.sh (Refactored)

    #!/bin/bash
    
    execute() {
    
        local -r CMDS="$1"
    
        local -r EXIT_STATUS_FILE="$(mktemp /tmp/XXXXX)"
    
        local exitCode=0
        local cmdsPID=""
    
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    
        # Execute a command within a 
        # a new 'x-terminal-emulator' window.
    
        x-terminal-emulator -e "($CMDS) ; echo \$? > $EXIT_STATUS_FILE" \
            &> /dev/null
    
        # Get the PID of the process spawned by
        # 'x-terminal-emulator'.
    
        cmdsPID="$(ps ax | grep -v "grep" | grep -v "S+" | grep "sh -c" | grep "$CMDS" | xargs | cut -d ' ' -f 1)"
    
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    
        # Wait for the commands to no longer be executing
        # in the background, and then get their exit code.
    
        until [ -s "$EXIT_STATUS_FILE" ];
        do
            sleep 1
        done
    
        exitCode="$(cat "$EXIT_STATUS_FILE")"
    
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    
        # Print output based on what happened.
    
        echo "$exitCode"
    
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    
        # Remove temporary file.
    
        rm -rf "$EXIT_STATUS_FILE"  
    
    }
    
    execute \
      "apt update && apt upgrade -y && apt full-upgrade -y && apt autoremove -y && apt clean"