Por que a função não retorna até que o processo em segundo plano termine?

21

Considere este script:

#!/bin/bash
function start {
  leafpad &
  echo $!
}
PID=$(start)
echo "PID is $PID"

O script não continua além de sua chave de fechamento até o processo do leafpad terminar, mesmo que seja um processo em segundo plano.

Por que é isso? É possível iniciar um processo em segundo plano a partir de uma função?

user234565
fonte

Respostas:

22

A função retorna, mas a substituição do comando é bloqueada, porque você criou um trabalho em segundo plano, mas ainda tem o stdout fd aberto. Basta fechá-lo adicionando >/dev/nullantes do &.

#!/bin/bash
function start {
  leafpad >/dev/null &
  echo $!
}
PID=$(start)
echo "PID is $PID"

Se você deseja que seu processo também tenha stdin, stdout, stderr fechado, use o seguinte:

leafpad >/dev/null 0>&1 2>&1 &

Isso fechará stdin (0), stdout (1) e stderr (2) e depois o plano de fundo (&). Além disso, ao usar esses redirecionamentos de fluxo , não se esqueça de que eles são "enganados", ou seja, duplicados na ordem de execução.

1>/dev/null 2>&1

e

2>&1 1>/dev/null

não são os mesmos ! No primeiro, você está duplicando um fluxo para / dev / null (que é o que você deseja); no segundo, você está duplicando / dev / stdout no stderr e, em seguida, fechando o stdout. Portanto, qualquer mensagem enviada para stderraparecerá no seu console.

Adrien M.
fonte
Confirmado no meu sistema
user120161
10
Você não está fechando os fluxos, está os redirecionando.
dcat 27/07
4
fechar; n>&-Onde nestá o descritor de arquivo.
dcat 27/07
1
@dcat: Sim, mas o redirecionamento de / para /dev/nullnão leva a erros de E / S quando um processo tenta gravar seu stdout, mas descobre que 1é um FD inválido. Portanto, a terminologia no post está errada, não a programação real do bash. (Na verdade, duplicando FD 1 a 0 significa que stdin será um arquivo descritor aberto com O_RDONLY, o que provavelmente vai dar um erro (em vez do desejado no-bytes disponível no mercado) quando as tentativas do processo para ler.), Por exemplo wc >/dev/null 0>&1->wc: standard input: Bad file descriptor
Peter Cordes
1
@ PeterCordes - O fechamento do descritor antigo e o redirecionamento do novo não precisam ser mutuamente exclusivos. exec <&- >&- <>/dev/null >&0lida com stdin / out bastante exaustivamente. Faz diferença zshpelo menos o que concatenará todas as aberturas no mesmo descritor automaticamente quando multios estiver definido.
mikeserv