3
$ cat test1.sh
#!/bin/bash

setsid sleep 100

'test1.sh' shell script will not exit at once.

$ cat test2.sh
#!/bin/bash

setsid sleep 100 &

'test2.sh' shell script will exit at once.

Could anyone explain for me? Thanks a lot.

fedorqui
  • 275,237
  • 103
  • 548
  • 598

2 Answers2

3

The question is probably a duplicate of this even older question, and the answer over there is quite useful. Perhaps these should be merged.

I was also surprised by this behaviour of setsid. So I checked the man page:

DESCRIPTION
       setsid runs a program in a new session. The command calls fork(2) if already a
       process group leader. Otherwise, it executes a program in the current process.
       This default behavior is possible to override by the --fork option.

Some jargon to understand here. Let's check the POSIX definitions list (chapter 3).

A session is:

A collection of process groups established for job control purposes. Each process group is a member of a session. A process is considered to be a member of the session of which its process group is a member. A newly created process joins the session of its creator. A process can alter its session membership; see setsid(). There can be multiple process groups in the same session.

The fork() call creates a new process by copying the current process. More info in this post.

A process group leader is:

A process whose process ID is the same as its process group ID.

So, setsid will only create a new process if it is executed as a process group leader. This is always the case when running a command directly in the shell, so that the job queue can be constructed as a list of process groups. However, when run as part of a script, the group leader is the new shell process used to run the script.

Luckily, we have the --fork option to force creation of a new process. For example, the shell script:

#!/bin/env bash
setsid --fork sleep 5

will exit without waiting for sleep to finish, and the sleep command will continue even if you close the shell from which the script was run. As far as I am aware, setsid --fork is the only way to properly ensure that a command is run as a detached process.

Other resources:

adigitoleo
  • 92
  • 1
  • 8
  • 1
    Excellent answer, but as of 20230721 setsid is not part of posix shell utilities: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/contents.html. `#!/usr/bin/env bash` looks better to me, because posix neither mandates where to install utilities. – Jay-Pi Jul 21 '23 at 10:04
0

Your first script waits for setsid sleep 100 to be finished and then returns (in other words it will return after sleep finishes, 100 seconds here), but the 2nd script don't waits for the command to finish, cause & puts the command in background and returns immediately. To quote man bash:

If  a  command is terminated by the control operator &, the shell executes the
command in the background in a subshell. The shell does not wait for the command
to finish, and the return status is 0.

Also note that & is a control operator and it has higher precedence. Hope this helps!

rakib_
  • 136,911
  • 4
  • 20
  • 26
  • Thank you for your reply. I know the usage of '&' in shell script. If 'setsid' is removed, your answer is very good. But 'setsid' is used here. Can you explain it to me from the 'setsid' point? – user3015856 Jan 22 '14 at 07:24