运行异步任务并在 bash 中检索其退出代码和输出

Inf*_*ien 5 linux command-line bash shell-script process-substitution

我必须异步运行一堆 bash 命令,一旦完成,我需要根据其退出代码和输出执行操作。请注意,我无法预测这些任务在我的实际用例中将运行多长时间。

为了解决这个问题,我最终使用了以下算法:

For each task to be run:
    Run the task asynchronously;
    Append the task to the list of running tasks.
End For.

While there still are tasks in the list of running tasks:
    For each task in the list of running tasks:
        If the task has ended:
            Retrieve the task's exit code and output;
            Remove the task from the list of running tasks.
        End If.
    End For
End While.
Run Code Online (Sandbox Code Playgroud)

这给了我以下 bash 脚本:

For each task to be run:
    Run the task asynchronously;
    Append the task to the list of running tasks.
End For.

While there still are tasks in the list of running tasks:
    For each task in the list of running tasks:
        If the task has ended:
            Retrieve the task's exit code and output;
            Remove the task from the list of running tasks.
        End If.
    End For
End While.
Run Code Online (Sandbox Code Playgroud)

输出告诉我wait尝试检索每个任务的退出代码失败,除了要运行的最后一个:

Asynchronous commands:
PID     FD
4348    10
4349    11
4351    12
4353    13
4355    14
4357    15
4359    16
4361    17
4363    18
4365    19

Exit codes and outputs:
PID     FD  EXIT OUTPUT
./bg.sh: line 29: wait: pid 4348 is not a child of this shell
4348    10  127  16010
./bg.sh: line 29: wait: pid 4349 is not a child of this shell
4349    11  127  8341
./bg.sh: line 29: wait: pid 4351 is not a child of this shell
4351    12  127  13814
./bg.sh: line 29: wait: pid 4353 is not a child of this shell
4353    13  127  3775
./bg.sh: line 29: wait: pid 4355 is not a child of this shell
4355    14  127  2309
./bg.sh: line 29: wait: pid 4357 is not a child of this shell
4357    15  127  32203
./bg.sh: line 29: wait: pid 4359 is not a child of this shell
4359    16  127  5907
./bg.sh: line 29: wait: pid 4361 is not a child of this shell
4361    17  127  31849
./bg.sh: line 29: wait: pid 4363 is not a child of this shell
4363    18  127  28920
4365    19  10   28810
Run Code Online (Sandbox Code Playgroud)

命令的输出被完美地检索到,但我不明白这个is not a child of this shell错误来自哪里。我一定是做错了什么,因为wait能够异步运行最后一个命令的退出代码。

有谁知道这个错误来自哪里?我对这个问题的解决方案是有缺陷的,还是我误解了 bash 的行为?我很难理解wait.

PS:我在超级用户上发布了这个问题,但转念一想,它可能更适合 Unix & Linux Stack Exchange。

小智 4

这是一个错误/限制;bash 只允许等待最后一个进程替换,无论您是否将 的值保存$!到另一个变量中。

更简单的测试用例:

$ cat script
exec 7< <(sleep .2); pid7=$!
exec 8< <(sleep .2); pid8=$!
echo $pid7 $pid8
echo $(pgrep -P $$)
wait $pid7
wait $pid8

$ bash script
6030 6031
6030 6031
/tmp/sho: line 9: wait: pid 6030 is not a child of this shell
Run Code Online (Sandbox Code Playgroud)

尽管pgrep -P实际上发现它是 shell 的子级,并且strace表明它bash实际上正在收获它。

但无论如何,$!也设置为最后一个进程替换的 PID 是一个未记录的功能(iirc 在旧版本中不使用该功能),并且会遇到一些问题。


发生这种情况是因为 bash 仅跟踪last_procsub_child变量中的最后一个进程替换。这是wait寻找 pid 的地方:

$ cat script
exec 7< <(sleep .2); pid7=$!
exec 8< <(sleep .2); pid8=$!
echo $pid7 $pid8
echo $(pgrep -P $$)
wait $pid7
wait $pid8

$ bash script
6030 6031
6030 6031
/tmp/sho: line 9: wait: pid 6030 is not a child of this shell
Run Code Online (Sandbox Code Playgroud)

但当创建新的 proc subst 时,它将被丢弃:

-- jobs.c --
/* Return the pipeline that PID belongs to.  Note that the pipeline
   doesn't have to belong to a job.  Must be called with SIGCHLD blocked.
   If JOBP is non-null, return the index of the job containing PID.  */
static PROCESS *
find_pipeline (pid, alive_only, jobp)
     pid_t pid;
     int alive_only;
     int *jobp;         /* index into jobs list or NO_JOB */
{
     ...
  /* Now look in the last process substitution pipeline, since that sets $! */
  if (last_procsub_child)
    {
Run Code Online (Sandbox Code Playgroud)