Reputation: 10993
What happens if you have multiple exec
commands in a shell script, for example:
#!/bin/sh
exec yes > /dev/null &
exec yes alex > /dev/null
I assume that a fork is still needed in order to execute the first command since the shell needs to continue executing?
Or does the &
specify to create a sub process in which the exec is actually then run?
Upvotes: 4
Views: 4022
Reputation: 70832
The use of &
implie a sub-process.
Background Commands – & If a command is terminated by the control operator ampersand (&), the shell executes the command asynchronously – that is, the shell does not wait for the command to finish before executing the next command.
But exec
is used to replace current process by command:
exec [command arg ...] Unless command is omitted, the shell process is replaced with the specified program (which must be a real program, not a shell builtin or function). Any redirections on the exec command are marked as permanent
So this features are mutually exclusive! In syntaxe:
exec bash -c 'sleep 12' &
here exec
has no effect!
Demo:
export LANG=C
echo $$
17259
exec sh -c 'echo $$;read foo' &
[1] 17538
17538
[1]+ Stopped exec sh -c 'echo $$;read foo'
fg
exec sh -c 'echo $$;read foo'
17259
I run the script: echo $$;read foo
in order to prevent exit before having quietly read previous output.
In this sample, the current process ID is 17259
.
When run with ampersand (&
), the output is another pid (bigger). when run without ampersand, the new shell replace the command and is not forked.
Replacing the command by:
sh -c 'echo $$;set >/tmp/fork_test-$$.env;read'
re-running the whole test will generate two files in /tmp
.
On my desk, I could read:
19772
19994
19772
So I found two files in /tmp
:
-rw-r--r-- 1 user0 user0 2677 jan 22 00:26 /tmp/fork_test-19772.env
-rw-r--r-- 1 user0 user0 2689 jan 22 00:27 /tmp/fork_test-19994.env
If I run: diff /tmp/fork_test-19*env
, I read:
29c29
< SHLVL='0'
---
> SHLVL='1'
46a47
> _='/bin/sh'
So the first run, with ampersand is in a sublevel.
Nota: This was tested under many different shell.
ps --tty $(tty) fw
PID TTY STAT TIME COMMAND
82905 pts/9 Ss 0:00 /bin/bash
83360 pts/9 R+ 0:00 \_ ps --tty /dev/pts/9 fw
yes >/dev/null &
[1] 83860
exec yes >/dev/null &
[2] 84150
ps --tty $(tty) fw
PID TTY STAT TIME COMMAND
82905 pts/9 Ss 0:00 /bin/bash
83860 pts/9 R 0:32 \_ yes
84150 pts/9 R 0:11 \_ yes
84181 pts/9 R+ 0:00 \_ ps --tty /dev/pts/9 fw
There is no difference between process 83860
and 84150
(expect pid number and start time, asked for)!
ps --tty $(tty) ho pid,cmd | sed 's/y[e]s//p;d' |
xargs --no-run-if-empty --verbose kill
kill 83860 84150
[1]- Terminated yes > /dev/null
[2]+ Terminated exec yes > /dev/null
Upvotes: 2
Reputation: 531345
The shell forks to run the background process, but that means the new shell still needs to fork to run yes
. Using exec
eliminates the fork in the subshell.
Upvotes: 0