Quando você executa um trabalho em segundo plano, o bash imprime o ID do processo de seu subprocesso, aquele que executa o comando nesse trabalho. Se esse trabalho criar mais subprocessos, isso não é da conta do shell pai.
Quando o job em background é um pipeline (ou seja, o comando está no formato something1 | something2 &
, e não eg { something1 | something2; } &
), há uma otimização que é strongmente sugerida pelo POSIX e executada pela maioria dos shells incluindo o bash: Os elementos do pipeline são executados diretamente como subprocessos do shell original. O que o POSIX exige é que a variável $!
esteja definida como o último comando no pipeline neste caso. Na maioria das shells, o último comando é um subprocesso do processo original, assim como os outros comandos no pipeline.
Quando você executa ls -lsa | grep feb
, há três processos envolvidos: o que executa o lado esquerdo do canal (um subshell que termina de configurar o canal, em seguida, executa ls
), o que executa o direito lado direito do tubo (um subshell que termina de configurar o tubo, em seguida, executa grep
) e o processo original que aguarda o término do tubo.
Você pode acompanhar o que acontece rastreando os processos:
$ strace -f -e clone,wait4,pipe,execve,setpgid bash --norc
execve("/usr/local/bin/bash", ["bash", "--norc"], [/* 82 vars */]) = 0
setpgid(0, 24084) = 0
bash-4.3$ sleep 10 | sleep 20 &
…
Observe como o segundo sleep
é relatado e armazenado como $!
, mas o ID do grupo de processos é o primeiro sleep
. Dash tem a mesma estranheza, ksh e mksh não.