Bash脚本,在函数中调用时,不能等待eval命令完成

时间:2017-06-12 22:06:22

标签: bash

我要做的是创建一个通用的异步命令运行器,它允许我在后台运行命令并获取其输出和代码而不阻塞我正在使用的shell(想想串行)。对于大多数命令,我可以执行以下操作:

FUNCwaitForCommand() {
    wait "$1"
    echo $? > "code.txt"
}
ls > "output.txt" &
pid=$!
FUNCwaitForCommand $pid &

但是,这对组合命令不起作用,例如

(cat < somefifo)

我可以使用以下内容运行命令:

FUNCwaitForCommand() {                                                                                    
    wait $1                                                                                               
    echo $? > code                                                                                        
}
eval "ls > output.txt &"
pid=$!
FUNCwaitForCommand $pid &

但等待不等。我可以等待完成,直到完成该过程:

while kill -0 "$1"; do wait "$1"; done

而不是只是等待,但它给我的代码是127而不是运行的命令的代码。如果我在pid收集后直接等待

eval "ls > output.txt &"
pid=$!
wait $pid

它等待这个过程很好,但很明显它没有背景并将shell释放给我。

我对bash并不擅长,但看起来函数内部与eval不在同一个子shell中,所以它并不能识别后台进程,尽管我并不知道。我知道为什么它只在使用eval时才这样做,而不是在使用正常执行方法时。

1 个答案:

答案 0 :(得分:2)

解释

就像你不能使用:

sleep 5 & pid=$!
wait $pid &

你也不能把这种等待放在背景功能中。也就是说,你可以运行:

sleep 5 & pid=$!
waitForCommand() { wait "$@"; }
waitForCommand "$pid"

但你无法运行:

sleep 5 & pid=$!
waitForCommand() { wait "$@"; }
waitForCommand "$pid" &

这是因为进程对于他们的孩子只能wait()。当你用&从shell中分离出一个新孩子时,你不再是父母了 - 相反,你是一个兄弟姐妹。因此,这不是特定于shell的行为,而是通用UNIX语义 - 您将在任何语言中获得等效错误。

解决方法

确保退出状态记录由正在记录退出状态的进程的直接父进程完成,即使该父进程本身位于相对于原始shell的后台

tempdir_top=$(mktemp -t -d bgdir.XXXXXX)
declare -g -A tempdirs=( )

runBackgroundCommand() {
  (( "$#" == 1 )) || { echo "Usage: runBackgroundCommand 'command'" >&2; return 1; }
  local cmd tempdir
  cmd=$1
  tempdir=$(mktemp -d "$tempdir_top/proc.XXXXXX")
  {
    printf '%s\0' "$cmd" >"$tempdir/cmd"
    eval "$cmd" >"$tempdir/stdout" 2>"$tempdir/stderr" & pid=$!
    printf '%s\n' "$pid" >"$tempdir/pid"
    wait "$1"; retval=$?
    printf '%s\n' "$retval" >"$tempdir/retval"
  } &
  tempdirs[$tempdir]=$!
}

# example usage
runBackgroundCommand "sleep 5"
runBackgroundCommand "sleep 10"

这样,在父进程中,您有一个临时目录映射到每个顶级PID(很容易用于检查完成),并且可以查看该目录内部以获取有关所涉及的任何进程的更多信息