等待子进程但出现错误: 'pid不是该shell的子进程'

30

我编写了一个从HDFS并行获取数据的脚本,然后在一个for循环中等待这些子进程,但有时返回 pid is not a child of this shell 错误,有时却能正常工作。这让人感到困惑。我使用 jobs -l 命令显示所有在后台运行的任务,我确信这些pid是shell进程的子进程,并且使用 ps aux 确保这些pid没有被分配给其他进程。以下是我的脚本。

PID=()
FILE=()
let serial=0

while read index_tar
do
        echo $index_tar | grep index > /dev/null 2>&1

        if [[ $? -ne 0 ]]
        then
                continue
        fi

        suffix=`printf '%03d' $serial`
        mkdir input/output_$suffix
        $HADOOP_HOME/bin/hadoop fs -cat $index_tar | tar zxf - -C input/output_$suffix \
                && mv input/output_$suffix/index_* input/output_$suffix/index &

        PID[$serial]=$!
        FILE[$serial]=$index_tar

        let serial++

done < file.list

for((i=0;i<$serial;i++))
do
        wait ${PID[$i]}

        if [[ $? -ne 0 ]]
        then
                LOG "get ${FILE[$i]} failed, PID:${PID[$i]}"
                exit -1
        else
                LOG "get ${FILE[$i]} success, PID:${PID[$i]}"
        fi
done

一个好问题,我也遇到了完全相同的错误。我启动了96个后台作业并等待它们完成。其中96个中有4个给了我“pid 28991(此数字是随机子PID的示例)不是此shell的子进程”的错误。我猜测wait命令并不是万无一失的。我会进行一些调查。 - Kemin Zhou
3个回答

31

只需查找要等待的进程的进程ID,并将其替换为下面脚本中的12345。根据您的要求可以进行进一步更改。

#!/bin/sh
PID=12345
while [ -e /proc/$PID ]
do
    echo "Process: $PID is still running" >> /home/parv/waitAndRun.log
    sleep .6
done
echo "Process $PID has finished" >> /home/parv/waitAndRun.log

/usr/bin/waitingScript.sh

http://iamparv.blogspot.in/2013/10/unix-wait-for-running-process-not-child.html


聪明的技巧!可以这样等待,然后在进程完成后启动脚本。 - Viet
11
不太好,如果每个计算机程序都像这样使用轮询,那将是一件非常糟糕的事情 :) - Alexander Mills
8
我将无法获取子进程的退出代码。 - avisheks
5
整洁,我无法相信任何地方都没有标准工具来完成这个...我写了一个更简单的shell脚本叫做 waitpid ,基本上就是这个一行代码:while [ -e /proc/$1 ]; do sleep 1; done... - anarcat
@anarcat 有没有办法获取退出状态? - pmor
@pmor 我怀疑:通常只有父级才可以使用这个功能。 - anarcat

7

您的 while 循环或 for 循环运行在子 shell 中,因此您不能等待(parent、outer)shell 的子进程。

编辑 如果 while 循环或 for 循环实际上是:

(a) 在{...}块中 (b) 参与管道操作(例如:for....done|somepipe),则可能会出现这种情况。


你可以检查这种思路,例如在两个位置(以及脚本的顶层!)打印$BASHPID、$$和$BASH_SUBSHELL。 - sehe

6
如果您在容器中运行此命令,很可能是由于bash中的一个错误导致的,在容器环境中更容易遇到。根据bash源代码(特别是查看bash-4.2/jobs.c中有关RECYCLES_PIDSCHILD_MAX的注释),为了优化对后台作业的跟踪,他们使自己容易受到PID别名的攻击(其中新进程可能会遮盖旧进程的状态);为了缓解这种情况,他们修剪了他们的后台进程历史记录(显然是受POSIX强制规定?)。如果您想要wait等待已修剪的进程,则shell找不到它在历史记录中的信息,并且假设这意味着它从未知道过该进程(即“不是此shell的子进程”)。

我觉得我遇到了这个问题。你知道它是否会被修复吗? - Clete2
@Clete2 看起来已经这样8年了,而且这种恶化的行为似乎至少部分是由POSIX合规性所要求的。我不会指望它很快会改变。 - jhfrontz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接