了解后台变量分配和 wait 命令

了解后台变量分配和 wait 命令

其中man bash写道:

等待[-fn] [id ...]

等待每个指定的子进程并返回其终止状态。每个id可能是进程ID或作业规范;如果给出了作业规范,则等待该作业管道中的所有进程。如果未给出 id,则等待所有当前活动的子进程,并且返回状态为零。如果提供了 -n 选项,则 wait 等待任何作业终止并返回其退出状态。

典型的例子如

 command1 & command2 & command3 & wait

意味着这三个命令并行运行,并且只有在所有命令完成后才会完成下一步。

我的问题在于这两个 bash 脚本的结果:

#!/bin/bash
for i in 1 2 3 ; do
  a=$i
  echo "$a is $i"
done 2>/dev/null

结果:

1 is 1
2 is 2
3 is 3

和我预期的差不多。现在我假设分配变量是一个漫长的过程,所以我等待它:

#!/bin/bash
  for i in 1 2 3 ; do
  a=$i & wait
  echo "$a is $i"
done 2>/dev/null

结果是:

3 is 1
3 is 2
3 is 3

我很困惑,原因如下:

  1. 唯一wait应该等待完成的过程是分配变量,然后应该运行脚本中的下一步 ( echo)。a=3应该只发生在循环的最后一次迭代中。
  2. 据我所知,for-loops 在子 shell 中运行,并且wait仅具有启动它的 shell 的范围。因此它甚至不应该等待for-loop 完成(因为这是父级)。
  3. 我从未指定echo与其他进程并行运行,因此我没有预料到会出现竞争情况。

那么为什么a-variable 是在最后一次循环迭代中设置的,而该变量$i却不是?wait我误解了命令的哪一部分?这种行为完全超出了我的预期。

GNU bash,版本 5.0.3(1),基于 5.7.0-0.bpo.2-amd64 Linux 内核。

预先取消设置a会使第二个脚本返回此值

 is 1
 is 2
 is 3

即该变量从未设置,并且是从我之前的运行中拖出的。

答案1

for循环不在子shell中运行,例如您可以拥有for i in 1 2 3; do a=foo; done; echo $a并且它会打印foo,即使该变量在循环之外使用。循环变量的值也是循环最后分配给它的值(如果循环退出,则可能不是最后一个值break)。

但使用& 将该命令放入子 shell 中,然后执行操作a=$i &会使分配仅发生在子 shell 中。当然,您也可以在子 shell 中使用分配的值:

例如:

a=1;
{ a=2; echo "subshell: $a"; } &
echo "main: $a";
wait;
echo "after: $a"

会打印

main: 1
subshell: 2
after: 1

做一些类似{ a=$i; echo "$a"; } &验证的事情。

让后台命令访问(并修改!)主 shell 中的变量将需要进程之间进行一些同步,这会给 shell 带来复杂性,而收益可能相对较小。

如果你不过需要它,例如,如果您想在后台运行一个长时间运行的命令,但需要其输出,则必须执行一些操作,例如将输出临时存储在文件中,例如

tmp=$(mktemp -d)
some long process > "$tmp/a" &
another long process > "$tmp/b" &
wait
a=$(< "$tmp/a")
b=$(< "$tmp/b")
...
rm -rf "$tmp"

或者使用 GNU Parallel 之类的东西,它是专门为并行运行命令而构建的。它还具有parset能够为 shell 变量赋值的命令。

至于为什么你3$a循环的每次迭代中得到,这可能是因为你之前运行过第一个循环,并且它离开a3。将所有后续分配推入子 shell 中后,这就是a整个循环中主 shell中保留的值。

相关内容