如何获取父 shell 之外的 PID 的退出代码?

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

背景

我正在尝试获取通过 x-terminal-emulator -e 启动的命令的退出代码。使用此命令,它只需打开一个新终端 window,然后将您希望在新终端 window 中执行的命令传递给它 sh -c。然后我使用 ps ax, grep, xargscut 的组合来获取 运行 进程的 PID。

‍tester.sh

#!/bin/bash

execute() {

    local -r CMDS=""
    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"

⚠️错误

但是,当我使用命令wait等待PID完成运行以便我可以获得命令的退出代码时,waitreturns:

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

有没有一种方法既可以等待 PID 完成,又可以从未在父 shell 下生成的进程中获取其退出代码?

是否有理由在另一个 xterm 中启动您的命令? 您的脚本似乎是连续的,所以也许这个解决方案可以接受?

#!/bin/bash

execute() {

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

    output=`$CMDS > /dev/null`
    exitCode=$?
    echo $exitCode    
}
execute "apt update && apt upgrade -y && apt full-upgrade -y && apt autoremove -y && apt clean"

经过反复试验和一些研究,我想出了一个解决方案。

我的解决方案包括以下内容:

  1. 使用从 x-terminal-emulator 生成的新终端 window 中重定向 $? 输出到使用 mktemp.
  2. 的临时文件
  3. 然后,利用until条件结构等待临时文件不为空。
  4. 一旦临时文件不为空,我就可以 cat 文件的内容并将其存储在一个变量中。然后,echo结果。

‍tester.sh(重构)

#!/bin/bash

execute() {

    local -r CMDS=""

    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"

为什么要使用另一个终端而不创建 child 进程?恕我直言,它更干净。这样你就可以使用等待命令:

function i_exit {
        () &
        wait $!
        return $?
}

i_exit "true"
echo $?
i_exit "false"
echo $?
i_exit "true && true"
echo $?
i_exit "true && false"
echo $?

您使用“&”创建后台进程。创建的 PID 存储在 $!当 child 退出时,您可以 return 使用 $? 的值。这可以从具有相同变量的函数中捕获。上面的代码输出:

$ ./test.sh 
0
1
0
0

不需要ps aux | grep whatever

编辑: 当然,如果您真的愿意,您可以在终端中执行操作。以下脚本就是这样做的。我没有 xfce,但你可以用 "x-terminal-emulator" 替换 "xterm"。

#! /bin/bash

function i_exit {
        (xterm -e "") &
        wait $!
        return $?
}

# Waits 5 seconds
i_exit "true && echo 'Test 1' && sleep 5s"
echo $?
# Exits immediately
i_exit "false && echo 'Test 2' && sleep 5s"
echo $?