Como analisar este comando '{2 & 3 “$ @” &} 3 & 2 2 / dev / null'?

7

Há algumas semanas, vi uma resposta estranha sobre a questão " (Como) silenciosamente iniciar tarefa (s) em segundo plano? ". Essa solução parece incorreta ( cf minha resposta ) embora o shell pareça iniciar a tarefa silenciosamente em segundo plano.

eu. Problema: podemos realmente redirecionar o erro padrão do shell?

Não há explicação com a solução proposta e uma análise não fornece uma resposta confiável.

Abaixo, você pode ver o snippet de código.

# Run the command given by "$@" in the background
silent_background() {
   if [[ -n $BASH_VERSION ]]; then
      { 2>&3 "$@"& } 3>&2 2>/dev/null
   fi
}

O comando problemático é { 2>&3 "$@"& } 3>&2 2>/dev/null .

i) Analise

O grupo de comandos ( { ... } ) especifica dois redirecionamentos ( 3>&2 e 2>/dev/null ) para um comando ( # Run the command given by "$@" in the background ). O comando é uma lista assíncrona ( cmd& ) com um redirecionamento ( 2>&3 ).

especificação POSIX

Each redirection shall apply to all the commands within the compound command that do not explicitly override that redirection.

O redirecionamento de erro padrão 2>/dev/null é substituído pelo redirecionamento associado à lista assíncrona 2>&3 .

Casos concretos

No primeiro caso, o erro padrão é redirecionado para /dev/null , enquanto que, no segundo caso, o erro padrão do comando ainda está conectado ao terminal.

prompt% { grep warning system.log& } 2>/dev/null
prompt% { 2>&3 grep warning system.log& } 3>&2 2>/dev/null
grep: system.log: No such file or directory

Abaixo, podemos ver casos semelhantes. No primeiro caso, a saída padrão do comando ainda está anexada ao terminal. No segundo caso, o redirecionamento de saída padrão do comando é modificado: >echo.txt é substituído por >print.txt .

prompt% { >&3 echo some data...& } 3>&1 >echo.txt
[1] 3842
some data...
prompt% file echo.txt
echo.txt: empty
prompt% { >&3 echo some data...& } 3>print.txt >echo.txt
[1] 2765
prompt% file echo.txt
echo.txt: empty
prompt% cat print.txt
some data...

ii) Observações

Como mencionado anteriormente, o comando parece iniciar silenciosamente em segundo plano. Mais precisamente, a notificação sobre um trabalho em segundo plano, por exemplo [1] 3842 , não é exibido.

A análise anterior implica que os redirecionamentos são supérfluos, já que podem ser cancelados.

{ redir_3 cmd& } redir_1 redir_2 é equivalente a cmd& .

iii) Interpretação

Na minha opinião, a construção mencionada oculta a notificação devido a um efeito colateral.

Você pode explicar como isso acontece?

II. Hipótese (s)

A resposta e os comentários de Ilkkachu permitiram alguma progressão. Note que cada processo tem seus próprios descritores de arquivos.

  • As mensagens do shell podem ser enviadas no erro padrão do shell.

Outro contexto: Uma lista assíncrona executada em um subshell. O comando g não existe.

prompt% ( g& )
g: command not found

Asynchronous commands, commands grouped with parentheses,..., are executed in a subshell environment that is a duplicate of the shell environment...

Um subshell herda o valor de seu fluxo de erro padrão de seu shell pai.

Portanto, no caso anterior, seus fluxos de erro padrão devem se referir ao terminal. No entanto, a notificação do trabalho não é exibida, enquanto a mensagem de erro do shell provavelmente é exibida no erro padrão do shell.

    
por Fólkvangr 16.07.2018 / 15:30

4 respostas

10

Você perdeu o ponto mais importante, o redirecionamento de shell é aplicado da esquerda para a direita.

Em:

{ 2>&3 "$@"& } 3>&2 2>/dev/null

O comando do grupo inteiro é executado com:

  • Descritor de arquivo 3 = > erro padrão, que é terminal neste momento.
  • Descritor de arquivo 2 (erro padrão) = > / dev / null

Assim, quando o comando dentro do agrupamento é executado:

  • Erro padrão = > Descritor de arquivo 3, que é apontado para o terminal.

Portanto, se "$@"& imprimir qualquer coisa em seu erro padrão, a saída será impressa no terminal.

Para seus casos concretos:

{ grep warning system.log& } 2>/dev/null

{ grep warning system.log& } executado com erro padrão é apontado para /dev/null . grep não substitui nenhum redirecionamento, portanto, seu erro padrão é o mesmo com {...} e é redirecionado para /dev/null , você não obteve saída para o terminal.

Em:

{ 2>&3 grep warning system.log& } 3>&2 2>/dev/null
O erro padrão de

grep é redirecionado para o descritor de arquivo 3, que é apontado para o terminal como explicado acima, então você tem saída para o terminal.

    
por 16.07.2018 / 16:02
5

No Bash interativo, foo & executa foo no segundo plano e imprime seu id do job e id do processo no erro padrão do shell.

foo 2>/dev/null & runs foo em segundo plano, com seu stderr redirecionado para /dev/null , mas ainda imprime o id do job e o id do processo no erro padrão do shell (não o stderr redirecionado de %código%).

foo primeiro redireciona stderr para { foo & } 2>/dev/null , depois o interior do /dev/null é processado com esse redirecionamento aplicado. Então {} é iniciado em segundo plano, e o id do job e o id do processo são impressos no stderr agora redirecionado do shell (ou seja, para foo ).

O manual do Bash implica que os redirecionamentos fora do grupo se aplicam ao grupo como um todo :

When commands are grouped, redirections may be applied to the entire command list.

Podemos verificar o redirecionamento aplicado. O grupo também redireciona a saída do ID da tarefa:

$ { sleep 9 & } 2> temp
[ no output here ]
$ cat temp
[1] 8490

Quando o comando eventualmente sai, o aviso disso aparece no terminal, no entanto: (Ou melhor, no stderr atual do shell, o que quer que seja.)

$ kill %1
[1]+  Terminated              sleep 99

Em /dev/null , o fd 3 é redirecionado para onde stderr apontar para agora, então stderr é redirecionado para { ... } 3>&2 2>/dev/null . Assumindo que stderr originalmente apontou para o terminal, o resultado é /dev/null . Esses redirecionamentos se aplicam ao grupo.

Se tivermos 3 -> terminal, 2 -> /dev/null dentro do grupo, { foo 2>&3 & } é iniciado em segundo plano, com o stderr apontado para o fd 3 do grupo, e o id do job e o id do processo são impressos no stderr do grupo.

Colocando esses dois juntos, o stderr de foo é apontado para onde o fd 3 do grupo aponta, ou seja, o stderr original e o ID do trabalho é impresso no fd 2 do grupo, ou seja, foo .

Portanto, com efeito, /dev/null redireciona o ID do job e o ID do processo impressos pelo shell para { 2>&3 foo & } 3>&2 2>/dev/null e direciona o stderr do /dev/null em torno desse redirecionamento:

foo's stdout      -> group's fd 1 -> original stdout (never redirected)
foo's stderr      -> group's fd 3 -> original stderr
job id from group -> group's fd 2 -> /dev/null
    
por 16.07.2018 / 23:28
1

Você está perdendo que o comando { é executado com um stderr anulado, ele inicia os comandos de fundo contidos com o fd restaurado, mas é ele quem inicia os comandos e emite as mensagens de feedback iniciado pelo comando, e corre com o nulled.

    
por 16.07.2018 / 15:44
0

O princípio do comando estudado é canalizar temporariamente o fluxo stderr do shell para /dev/null para cancelar a notificação do shell.

A solução é complexa: não especificada e contra-intuitiva . De fato, a documentação do Bash não menciona explicitamente, nem descreve, qualquer redirecionamento de shell, e os redirecionamentos são frequentemente aplicados aos comandos.

  • Como a solução funciona?

O comando de agrupamento {} afeta o ambiente atual do shell e os redirecionamentos também podem ser usados para modificar identificadores de arquivos no ambiente de execução do shell atual. Em outras palavras, os fluxos podem ser redirecionados temporariamente durante a execução do comando. Finalmente, um subshell é uma duplicata do ambiente do shell.

O fluxo stderr do shell é redirecionado para /dev/null , portanto, o stderr da subshell não está conectado ao monitor.

Referências

  • Bash Reference Manual, capítulo "Basic Shell Features", seção "Shell Commands", subseção "Compound Commands", subseção "Grouping Commands".
  • Bash Reference Manual, capítulo "Basic Shell Features", seção "Redirections".
  • Bash Reference Manual, capítulo "Basic Shell Features", seção "Executing Commands", subseção "Command Execution Environment".
por 19.07.2018 / 19:54