Bash 4.4.19(1)-发布
我有一个简单的脚本,它是日志记录应用程序的基础。由于各种原因,我不得不使用进程替换。
这runner
是应用程序的核心,由于进程替换是异步的,我已经设法通过while
循环使其达到很好的一致性。它完美地工作。
不幸的是,我发现了一个不起作用的情况:当我执行 ' bash <filename> <function>
'
所以我们需要2个文件来重现。
要求:
- 为什么会这样?
- 如何修改我的 while 循环以适应类似情况?
简化的脚本是:
test.sh
#!/bin/bash
2sub() {
local in=$(cat); echo -e "$in";
}
runner () {
"${@}" 1> >(2sub)
while [ -e /proc/$! ]; do sleep 0.1; done # <<< LOOP WAIT FOR $!
}
remotesub() {
bash ./test2.sh remotesub2
}
echo -e "running\n";
runner bash ./test2.sh remotesub2 # LOOPS
# runner remotesub # A POSSIBLE BYPASS/SOLUTION? But why?
echo -e "done!\n"
test2.sh
remotesub2() {
echo -e "'${BASH_VERSION}'"
return 0
}
"$@"
旁路:
正如您从脚本中看到的那样,通过bash <filename> <function>
在函数内部包含并将函数传递给runner
. 为什么这是有效的,而不是直接的方式,我相信这里有人知道。
请阐明这个问题,以及是否有更好的方法来执行等待循环以涵盖这些情况。
解决方案:
最好的解决方案是 mosvy 建议的。谢谢你。使用 { "${@}"; }
消除了将命令打包在单独的小函数中的需要,这是一种痛苦。同样在用我的较大代码进行了数小时的测试后,我得出结论,仔细杀死子进程使得这while [ -e /proc/$! ]; do sleep 0.1; done
没有必要。那条线被替换为wait $!;
如果我完全理解你,你想知道为什么只有当它是内置命令或函数的命令行的一部分时
$!
才会设置为在内部运行的进程的 PID ,而不是当它是命令行的一部分时>(...)
外部命令。简化示例:
发生这种情况是因为在使用外部命令的情况下,
bash
将派生一个单独的进程来运行它,并且在其中运行的进程>(...)
将作为该进程的子进程运行,因此作为脚本的孙子进程,完全在外部对其控制。到外部命令终止时,它的子命令(如果仍在运行)将被 pid 1 (init) 采用,因此任何仍可用于从脚本中检索其 PID 的链接都已损坏。
一种解决方法可能是使用包装函数,该函数将导致其命令行中的所有进程替换作为脚本的子进程运行,因此它们的 PID 可以通过
pgrep -P "$$"
.此外,将外部命令放入
{...}
块中并重定向块的输出似乎也有效:两种解决方法都依赖于当前实现的工作方式;例如。
bash
有一天可能会决定优化掉琐碎的组命令或功能,打破这些假设。请注意,
$!
从最后一个进程替换中设置为 PID 是一个未记录的功能,该功能在bash
.