Walker Boh
Walker Boh

Reputation: 770

When executing a bash script with Php exec(), why does Php wait for disowned background jobs to complete?

I could use some help understanding why this is happening. I am running a php script from command line that launches a bash script via exec(). In the bash script, I'm creating a process in the background (e.g. sleep 5; echo "Hello" &). I am expecting php to launch the bash script and then move on once it's done. However, php seems to be waiting for the background process launched by the bash script to complete before progressing through the rest of the script

To demonstrate, I'll use the following 3 example scripts in a linux shell environment:

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"

Now, running php runstuff.php (from the command line) will "hang" until test-script.sh completes and then php progresses through the rest of the script. However, running ./stuff.sh directly will immediately return to the command line prompt and the output appears 5 seconds later.

Look at the process list when running runstuff.php, I can see that test-script.sh is NOT a child of either runstuff.php or stuff and stuff.sh goes defunct:

 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

When running ./stuff.sh directly, stuff.sh looks fine.

27963 pts/3    S      0:00 /bin/bash ./stuff.sh
27965 pts/3    S      0:00  \_ sleep 5

So my question is this: Why is stuff.sh hanging around when executed through Php exec() when it doesn't hang around when running the script directly? Is Php somehow still recognizing test-script.sh as a child process even though it's detached from stuff.sh?

Also, it seems that modifying stuff.sh to use &> /dev/null & instead of just & seems to work as expected.

modified stuff.sh

#!/bin/bash

echo "stuff.sh running background process..."
./test-script.sh &> /dev/null &
echo "stuff.sh done"

So why does redirecting output to /dev/null produce the expected results?

Upvotes: 3

Views: 2318

Answers (1)

that other guy
that other guy

Reputation: 123550

PHP exec(..) opens a pipe to for the command, and reads from it until it closes, returning the data as a string (which you don't use).

Pipes don't close until all writers have closed it, so any background process with the pipe open as stdout will keep it open. Therefore, exec will keep waiting for them, thinking there may be more output. It's a good thing too: five seconds later, it reads a "test-script.sh COMPLETE" message which would otherwise have disappeared.

When you redirect stdout to /dev/null instead for the background process, it will not keep the pipe open, and exec therefore returns immediately since there can't possibly be any more output.

Upvotes: 4

Related Questions