2

I wonder how shell executes this code in .sh file(like test.sh):

while true
do
    # check php process. If not running, run it. 
    cd $some_dir && php -f some_file.php &
    # sleep 1s
done

Some output:

ps -ef|grep test|grep -v grep
root     10963  3040  0 11:13 pts/19   00:00:00 /bin/bash ./test.sh start
root     10973 10963  0 11:13 pts/19   00:00:00 /bin/bash ./test.sh start
root     10975 10973  0 11:13 pts/19   00:00:00 php -f test_loop.php

In my case, there are three processes, including 2 test.sh and 1 php. But if use the following code, that is, in two lines or parentheses, it's ok:

cd $some_dir && (php -f some_file.php &)

or

cd $some_dir
php -f some_file.php &

output:

ps -ef|grep test|grep -v grep
root     11112  3040  0 11:14 pts/19   00:00:00 /bin/bash ./test.sh start
root     11122 11112  0 11:14 pts/19   00:00:00 php -f test_loop.php

In this case, two kinds of codes are executed in a subshell, which is what we expect.

In the first case, it seems that there is an intermediate process between the original process and php process. So what's it and why is it created?

Max
  • 547
  • 4
  • 9
  • 2
    Compare to `cd "$some_dir" && exec php -f some_file.php &` -- the `exec` instructs the subshell to replace itself with the `php` process, rather than waiting for it to exit, and thus prevents the subshell from remaining in memory while `php` runs. (That said, the performance and memory usage impact is negligible either way -- keep in mind that `fork()`ing a process in two doesn't actually use twice the memory, since the pages are flagged to be copied when changes are written; thus, there's very little practical difference between these approaches). – Charles Duffy Oct 26 '18 at 03:14
  • BTW, an addendum to an answer generally belongs somewhere other than in a question edit -- editing answers into questions, even for purposes of commenting on them, privileges those answers over others, thus preventing other answers from being on an even playing field (wrt presentation based on community voting). You might consider a community-wiki supplemental answer, or just a comment on the existing 3rd-party answer. – Charles Duffy Oct 26 '18 at 03:17
  • Good point! And thanks for your reminding. – Max Oct 26 '18 at 03:19

1 Answers1

1

The statement

cd $some_dir && php -f some_file.php &

runs in a subshell and that's the reason you see an extra process. In your output, PID 10963 is the original (parent) shell and 10973 is the subshell whose child is the PHP process. The grandparent (PID 10963) is active because of the infinite loop.

When you remove &&, the two statements run in the parent shell itself and hence you don't see the extra process.

codeforester
  • 39,467
  • 16
  • 112
  • 140
  • This is exactly what I guess. But why `&&` statement is executed in a subshell? – Max Oct 25 '18 at 03:35
  • 1
    Because of `&&` - subshell is invoked because of the compound command. – codeforester Oct 25 '18 at 03:37
  • Hi codeforester, do you have any authoritative documents about this? – Max Oct 25 '18 at 03:42
  • I checked Bash manual - didn't see any explicit references to it. The manual does say command ending with `&` is executed in a subshell. – codeforester Oct 25 '18 at 03:47
  • Yes, `&` puts php in background, but it's the second case. – Max Oct 25 '18 at 03:51
  • You have an `&` inside the while loop (first code) as well. – codeforester Oct 25 '18 at 03:59
  • See [man Bash](http://man7.org/linux/man-pages/man1/bash.1.html) under `"Lists"` section, 5th paragraph. `&&` connects `"pipelines"` which are separate processes where the `stdout` of the first is connected to the `stdin` of the second. The `php` command is only executed following the successful completion of the `cd` command. – David C. Rankin Oct 25 '18 at 04:21
  • 4
    @DavidC.Rankin You can check whether this is the case with `sleep 100 && true &`. It's interpreted as `( sleep 100 && true ) &` , not `sleep 100 && ( true & )` – that other guy Oct 25 '18 at 04:23
  • I guess it is more semantics. If the `cd` (or `sleep` in your example) fails, then the second command is never executed. So yes, point taken, `sleep` is backgrounded, and therefore the `cd` would be as well. – David C. Rankin Oct 25 '18 at 04:25
  • 1
    I suspect this is happening because Bash doesn't realize that it can skip the fork when running `php` in this case. Dash does not have this behavior. – that other guy Oct 25 '18 at 04:42
  • I agree with you, @that other guy. – Max Oct 25 '18 at 07:41
  • We can use the following code to show the subshell's PID obviously: `cd $some_dir && echo "subshell pid "$BASHPID && php -f $FILE &` – Max Oct 26 '18 at 03:33
  • 1
    BTW, `echo "subshell pid "$BASHPID` is quoting the content that *doesn't* need to be quoted, and leaving the content that *does* need to be quoted (to have an absolute assurance of correct output) unquoted. Try running your code after `IFS=0123456789` -- you'll see that the `BASHPID` value disappears. Quoting your expansions, as in `echo subshell pid "$BASHPID"` or `echo "subshell pid $BASHPID"`, prevents that and similar issues. – Charles Duffy Oct 26 '18 at 15:13