Alex Rothberg
Alex Rothberg

Reputation: 10993

Multiple exec in a shell script

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

Answers (2)

F. Hauri  - Give Up GitHub
F. Hauri - Give Up GitHub

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 .

Another demo:

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

chepner
chepner

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

Related Questions