在子 shell 中杀死 `sleep`

在子 shell 中杀死 `sleep`

在 bash 中,我观察到调用kill睡眠进程惯于 如果我从子 shell 开始,就杀死它。你能帮我理解为什么吗?

function foo () {
  # Start a loop with `sleep` in the background
  while true; do
    >&2 echo looping
    sleep 5
  done &
  # Wait for user input, then kill the "sleep" loop
  loop_id=$!
  read -p 'press enter' DUMMY
  kill $loop_id
}

echo $(foo) # Call foo within a subshell
echo "DONE"

可能会发生两种截然不同的情况,具体取决于最后一行是否使用子 shell

  • 使用子 shell 调用echo $(foo),如果我按 Enter 来满足read调用,那么脚本直到sleep结束后才会终止。
  • 如果我使用 justfoo而不是调用该函数$(foo),那么一旦我按 Enter 键,脚本就会终止。

我确实在这个玩具示例中,但在我的实际工作中,我想将标准输出捕获foo到 shell 变量中,所以我想尽快杀死子 shell。有没有办法让我同时实现这两个目标?

答案1

当您使用命令替换时$(foo),shell 需要等待,直到收到命令替换的所有输入。您sleep正在运行的文件描述符(可能是管道)的副本连接到 shell 读取命令替换输出的位置。 (我也很确定它将$!给出运行循环的 shell 的 PID while,而不是sleep其内部的 PID。)

考虑例如这个脚本:

foo() {
    sleep 5 &
    echo foo
}
tmp=$(foo)
echo end.

运行需要5秒。

但是将该行更改sleepsleep 5 > /dev/null &,现在脚本立即返回,因为sleep不再连接到命令替换。请注意,后台睡眠仍将在后台运行,直到超时。你只是不太可能注意到它。

请注意,这是特定于命令替换,不仅仅是任何子 shell 环境。 ((foo)也可以是一个子 shell,但应该与{ foo; }, 或仅foo在此处工作。)

相关内容