我可以使用一些帮助来理解为什么会发生这种情况。我从命令行运行一个php脚本,通过exec()启动一个bash脚本。在bash脚本中,我在后台创建了一个进程(例如sleep 5; echo "Hello" &
)。我期望php启动bash脚本,然后在完成后继续前进。然而,php似乎在等待bash脚本启动的后台进程完成,然后再继续执行脚本的其余部分
runstuff.php
<?php
echo "PHP running stuff.sh..." . PHP_EOL;
exec('./stuff.sh &');
echo "PHP done running stuff.sh..." . PHP_EOL;
stuff.sh
#!/bin/bash
echo "stuff.sh running background process..."
# This can be a script or any process placed in the background
./test-script.sh &
echo "stuff.sh done"
test-script.sh
#!/bin/bash
echo "test-script.sh waiting 5 seconds..."
sleep 5
echo "test-script.sh COMPLETE"
现在,运行php runstuff.php
(从命令行)将"挂起",直到test-script.sh完成,然后php继续执行脚本的其余部分。直接执行./stuff.sh
命令会立即返回命令行提示符,5秒后才会出现输出。
查看运行runstuff.php时的进程列表,我可以看到test-script.sh不是runstuff.php或stuff的子进程,而stuff.sh则失效了:
5873 pts/3 Ss 0:01 _ bash
27389 pts/3 S+ 0:00 | _ php runstuff.php
27390 pts/3 Z+ 0:00 | _ [stuff.sh] <defunct>
27392 pts/3 S+ 0:00 /bin/bash ./stuff.sh
27393 pts/3 S+ 0:00 _ sleep 5
直接运行./stuff.sh
时,stuff.sh看起来很好。
27963 pts/3 S 0:00 /bin/bash ./stuff.sh
27965 pts/3 S 0:00 _ sleep 5
所以我的问题是:为什么stuff.sh在通过Php exec()执行时挂在周围,当它不挂在直接运行脚本时?Php是否仍将test-script.sh识别为子进程,尽管它已与stuff.sh分离?
同样,修改stuff.sh使用&> /dev/null &
而不是&
似乎也能像预期的那样工作。
修改stuff.sh
#!/bin/bash
echo "stuff.sh running background process..."
./test-script.sh &> /dev/null &
echo "stuff.sh done"
那么,为什么将输出重定向到/dev/null会产生预期的结果呢? PHP exec(..)
为命令打开一个管道,并从中读取直到它关闭,以字符串形式返回数据(您不使用)。
管道在所有编者关闭之前不会关闭,因此任何将管道打开为标准输出的后台进程都将使其保持打开状态。因此,exec
会一直等待他们,认为可能会有更多的输出。这也是一件好事:五秒钟后,它读取了一条"test-script.sh COMPLETE"消息,否则该消息将会消失。
当您为后台进程将stdout重定向到/dev/null
时,它不会保持管道打开,并且exec
因此立即返回,因为不可能有任何更多的输出。