Bash脚本,can';在函数中调用时,不要等待eval命令完成
我试图做的是创建一个通用的异步命令运行程序,它允许我在后台运行命令,并在不阻塞我正在使用的shell的情况下获取其输出和代码(想想串行)。对于大多数命令,我可以执行以下操作:Bash脚本,can';在函数中调用时,不要等待eval命令完成,bash,Bash,我试图做的是创建一个通用的异步命令运行程序,它允许我在后台运行命令,并在不阻塞我正在使用的shell的情况下获取其输出和代码(想想串行)。对于大多数命令,我可以执行以下操作: FUNCwaitForCommand() { wait "$1" echo $? > "code.txt" } ls > "output.txt" & pid=$! FUNCwaitForCommand $pid & FUNCwaitForCommand() {
FUNCwaitForCommand() {
wait "$1"
echo $? > "code.txt"
}
ls > "output.txt" &
pid=$!
FUNCwaitForCommand $pid &
FUNCwaitForCommand() {
wait $1
echo $? > code
}
eval "ls > output.txt &"
pid=$!
FUNCwaitForCommand $pid &
但是,这不适用于组合命令,例如
(cat < somefifo)
但等待并不等待。我可以通过执行以下操作使其等待完成,直到流程完成:
while kill -0 "$1"; do wait "$1"; done
不是等待,而是它给我的代码是127,而不是运行的命令的代码。如果我在pid集合之后直接进行等待
eval "ls > output.txt &"
pid=$!
wait $pid
它等待的过程很好,但显然它没有背景和释放壳回给我
我不擅长bash,但它看起来好像函数内部与eval不在同一个子shell中,因此它无法识别后台进程,尽管我不知道为什么它只在使用eval时才这样做,而在使用正常执行方法时却不这样做。解释
正如您不能使用:
sleep 5 & pid=$!
wait $pid &
你也不能把等待放在后台函数中。也就是说,您可以运行:
但你不能跑:
这是因为进程只能为其子进程等待()。当你用&
把一个新的孩子从壳中分离出来时,你不再是父母,而是兄弟姐妹。因此,这不是特定于shell的行为,而是通用的UNIX语义——在任何语言中都会出现相同的错误
变通办法 确保退出状态记录由记录其退出状态的进程的直接父进程完成,即使该父进程本身位于相对于原始shell的后台 这样,在父进程中,每个进程都有一个临时目录到顶级PID的映射(很容易用于检查完成情况),并且可以在该目录中查找有关任何进程的更多信息。 正如您不能使用:
sleep 5 & pid=$!
wait $pid &
你也不能把等待放在后台函数中。也就是说,您可以运行:
但你不能跑:
这是因为进程只能为其子进程等待()。当你用&
把一个新的孩子从壳中分离出来时,你不再是父母,而是兄弟姐妹。因此,这不是特定于shell的行为,而是通用的UNIX语义——在任何语言中都会出现相同的错误
变通办法 确保退出状态记录由记录其退出状态的进程的直接父进程完成,即使该父进程本身位于相对于原始shell的后台
这样,在父进程中,每个进程都有一个临时目录到顶级PID的映射(很容易用于检查是否完成),并且可以在该目录中查找有关所涉及的任何进程的更多信息。与它作为一个函数无关,与函数相关的一切。也就是说,
FUNCwaitForCommand$pid&
不会像wait$pid&
那样工作。这不仅与函数无关,顺便说一句,也与eval
无关。请在将来更努力地使你的问题变得最小——简化复制机的任何方面,而这些方面实际上并不是重现问题所必需的。根据我的测试,我确实相信这是一个最小的例子。问题似乎在于,正如您所看到的,我认为它在不使用eval时有效。我记得今天早上早些时候在调试和制定这个问题时测试了它,我记得它起作用了。这段代码已经在生产环境中运行了几个月,这一事实支持了这一想法。然而,像往常一样,人脑并不像我们想象的那么可靠,所以我带来了不完整的信息。这与它是一个功能无关,一切都与你的功能背景有关。也就是说,FUNCwaitForCommand$pid&
不会像wait$pid&
那样工作。顺便说一句,这不仅与函数无关,也与eval
无关。请在将来更努力地使你的问题变得最小——简化复制机的任何方面,而这些方面实际上并不是重现问题所必需的。根据我的测试,我确实相信这是一个最小的例子。问题似乎在于,正如您所看到的,我认为它在不使用eval时有效。我记得今天早上早些时候在调试和制定这个问题时测试了它,我记得它起作用了。这段代码已经在生产环境中运行了几个月,这一事实支持了这一想法。然而,像往常一样,人脑并不像我们想象的那样可靠,所以我带来了不完整的信息。wait$pid
而不是wait“$1”
?谢谢,将pid写入一个文件是我最初完成这项工作所缺少的部分。不知道为什么我没有想到这一点,但利用这个想法,我能够让它正常工作。@codeforester,…所以,我确实在这个原始版本的函数中嵌套了这个错误(折叠到最初的5分钟编辑窗口中),但是,在添加您的评论时,答案中仍然存在的$pid
条目都是我想要的条目,因为它们要么是顶级的,要么是子shell中的作用域。wait$pid
而不是wait“$1”
?谢谢,将pid写入一个文件是我最初做这项工作所缺少的部分。不知道为什么我没有想到这一点,但利用这个想法,我能够让它正常工作。@codeforester,…所以,我确实在这个(折叠的)原始版本的函数中嵌套了这个错误
sleep 5 & pid=$!
waitForCommand() { wait "$@"; }
waitForCommand "$pid" &
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"