Job control refers to the protocol for allowing a user to move between multiple process groups (or jobs) within a single login session.
Generally it's enabled in interactive shells, and disabled in non-interactive ones:
$ echo $-; sleep 1 & fg
$ bash -c 'echo $-; sleep 1 & fg'
bash: line 1: fg: no job control
In this case... apparently job control is disabled, and
$- can't be relied upon:
$ (echo $-; sleep 1 & fg)
bash: fg: no job control
The shell associates a job with each pipeline.
That is, when job control is enabled each pipeline is executed in a separate process group.
ps -o pgid= $$
$ ./pgid.sh >&2 | ./pgid.sh >&2; ./pgid.sh; ./pgid.sh & wait
+ Done ./a.sh
$ (./pgid.sh >&2 | ./pgid.sh >&2; ./pgid.sh; ./pgid.sh & wait)
One of the jobs is a foreground job, the rest are background ones.
Background jobs are not supposed to be tied to the shell that started them. If you exit a shell, they will continue running. As such they shouldn't be interrupted by
SIGINT, not by default. When job control is enabled, that is fulfilled automatically, since background jobs are running in separate process groups. When job control is disabled,
bash makes the asynchronous commands ignore
SIGINT, and doesn't let them (if they're
bash scripts) override it.
That is, here:
$ bash -c "trap 'echo INT' INT; sleep 3" & pid=$!; sleep 1; kill -INT "$pid"; wait
the background job (
bash -c "trap 'echo INT' INT; sleep 3") is executed by an interactive shell, which has job control enabled. As a result the background job receives
When we wrap it into a non-interactive shell without job control:
$ (bash -c "trap 'echo INT' INT; sleep 3" & pid=$!; sleep 1; kill -INT "$pid"; wait)
bash -c "trap 'echo INT' INT; sleep 3" ignores
trap ... INT is also ignored.
This can be confirmed this way:
$ bash -c "trap 'echo INT' INT; trap; sleep 3" & pid=$!; sleep 1; kill -INT "$pid"; wait
trap -- 'echo INT' SIGINT
trap -- '' SIGFPE
+ Done bash -c "trap 'echo INT' INT; trap; sleep 3"
$ (bash -c "trap 'echo INT' INT; trap; sleep 3" & pid=$!; sleep 1; kill -INT "$pid"; wait)
trap -- '' SIGINT
trap -- '' SIGQUIT
trap -- '' SIGFPE
$ bash -c 'ps -o pid,ignored,comm,args -p $$' & wait
PID IGNORED COMMAND COMMAND
345833 0000000000000000 ps ps -o pid,ignored,comm,args -p 345833
+ Done bash -c 'ps -o pid,ignored,comm,args -p $$'
$ (bash -c 'ps -o pid,ignored,comm,args -p $$' & wait)
PID IGNORED COMMAND COMMAND
345629 0000000000000006 ps ps -o pid,ignored,comm,args -p 345629
A couple of relevant quotes:
Non-builtin commands started by Bash have signal handlers set to the values inherited by the shell from its parent. When job control is not in effect, asynchronous commands ignore
SIGQUIT in addition to these inherited handlers. Commands run as a result of command substitution ignore the keyboard-generated job control signals
Signals ignored upon entry to the shell cannot be trapped or reset.
Job control refers to the ability to selectively stop (suspend) the execution of processes and continue (resume) their execution at a later point. A user typically employs this facility via an interactive interface supplied jointly by the operating system kernel’s terminal driver and Bash.
The shell associates a job with each pipeline. It keeps a table of currently executing jobs, which may be listed with the
jobs command. When Bash starts a job asynchronously, it prints a line that looks like:
indicating that this job is job number
1 and that the process ID of the last process in the pipeline associated with this job is
25647. All of the processes in a single pipeline are members of the same job. Bash uses the job abstraction as the basis for job control.
To facilitate the implementation of the user interface to job control, the operating system maintains the notion of a current terminal process group ID. Members of this process group (processes whose process group ID is equal to the current terminal process group ID) receive keyboard-generated signals such as
SIGINT. These processes are said to be in the foreground. Background processes are those whose process group ID differs from the terminal’s; such processes are immune to keyboard-generated signals. Only foreground processes are allowed to read from or, if the user so specifies with
stty tostop, write to the terminal. Background processes which attempt to read from (write to when
stty tostop is in effect) the terminal are sent a
SIGTTOU) signal by the kernel’s terminal driver, which, unless caught, suspends the process.