Por favor ajude a explicar este redirecionamento de saída bash

4

Este script foi postado como resposta a um Pergunta . E estou tentando descobrir o que está acontecendo.

result=$(
  {
    {
      ssh host app-status >&3 3>&-; echo "$?"
    } | {
      until read -t1 ret; do
        printf . >&2
      done
      exit "$ret"
    }
  } 3>&1
)

Aqui está minha explicação, mas tenho certeza que está errado.

  1. O% externo {...} 3>&1 : fd 3 é aberto como uma duplicata de stdout
  2. Então, segunda metade do pipe, '| {} 'sem redirecionamento
  3. No stdout do printf do loop de leitura é duplicado com o stderr, assim no 2. (acima) toda a saída vem realmente através do stderr.
  4. Agora, a primeira metade do pipe, { ssh ... } | : stdout de ssh é dupida em fd3 (que é de fato stdout de 1. fd 3 é fechada com 3>&- , então stdout é reaberto no que era originalmente e isso é canalizado para 2.
  5. finalmente, echo apenas imprime para stdout

Então, minha pergunta (problema com entendimento) é; O resultado disso é o mesmo que apenas printf para stderr? O que o redirecionamento voodo para 3 realmente alcança? Há algo assíncrono aqui?

    
por X Tian 06.02.2014 / 13:30

3 respostas

4

Dentro de $(...) , stdout (fd 1) é um pipe. Na outra extremidade do pipe, o shell lê a saída e a armazena na variável $result .

Com: $({ blah; } 3>&1) , concluímos que tanto o fd 3 quanto o 1 apontam para esse pipe em blah .

blah é cmd1 | cmd2 . Há cmd1 e cmd2 são iniciados simultaneamente com cmd1 fd 1 apontando para outro canal (aquele para cmd2 ), mas não queremos que ssh output vá para esse canal, queremos que ele vá para o primeiro pipe para que ele possa ser armazenado em $result .

Portanto, temos { ssh >&3; echo "$?"; } | cmd2 , de modo que somente a saída echo vá para o canal até cmd2 . ssh output (fd 1) vai para $result . ssh não precisando de um fd 3, nós o fechamos para ele depois que usamos para definir fd 1 ( 3>&- ).

A entrada de cmd2 (fd 0) é o segundo tubo. Nada está gravando nesse pipe (desde que ssh está gravando no primeiro canal) até que ssh termine e echo mostre o status de saída lá.

Portanto, em cmd2 (sendo o loop until ), o read -t1 está aguardando com o tempo limite até que ssh seja encerrado. Depois disso, read retornará com sucesso com o conteúdo de $? alimentado por echo .

    
por 06.02.2014 / 13:45
1

Ok, primeiramente o que acontece se você executar um comando remoto via ssh

ssh  otherhost /bin/true; echo $?
0

e

ssh otherhost /bin/false; echo $?
1

so echo "$?" imprime o valor de retorno de app-status para stdout (file handle 1)

  1. O% outer {...} 3>&1 redireciona toda a entrada do identificador de arquivo 3 para stdout
  2. >&3 3>&- redireciona o stdout para o identificador de arquivo 3 e fecha o identificador de arquivo 3.
  3. read -t1 ret lê a entrada do stdin para a variável ret
  4. printf . >&2 imprime . para stderr (identificador de arquivo 2)

Boa leitura Guia avançado de scripts do bash

    
por 06.02.2014 / 13:51
0

A idéia básica por trás dos redirecionamentos voodo do fd 3 para o fd 1 e do fd 1 para o fd 3 no comando de grupo { ... 1>&3 3>&- ...} 3>&- 3>&1 é fazer com que a saída do comando ssh ignore o ssh | until pipe via fd 3.

Qualquer coisa escrita no fd 3 dentro do comando group com 1>&3 irá contornar o mecanismo do pipe.

Dessa forma, o comando read (que retornará "falha se uma linha completa de entrada não for lida dentro de TIMEOUT segundos", consulte help read no bash) só conseguirá ler o status de saída do ssh comando e a saída atribuída à variável result pode ser restrita à saída do comando ssh .

# examples for bypassing a pipe using output redirections

# cf. http://unix.stackexchange.com/a/18711
echo hello | sed 's/hello/HELLO/'
{ echo hello 1>&3 3>&- | sed 's/hello/HELLO/'; } 3>&- 3>&1

{
result=$(
  {
    {
      ssh localhost 'sleep 5; echo "ssh output"; exit 55' >&3 3>&-; echo "$?"
    } | {
      until read -t1 ret; do
        printf . >&2
      done
      printf '\n%s\n' "exit $ret" >&2
      exit "$ret"
    }
  } 3>&1
)
echo "result: $result"
}

# output:
# .....
# exit 55
# result: ssh output

E, por último, mas não menos importante, a duplicação de um fluxo stdout é feita por man 1 tee , não por redirecionamentos de saída. O redirecionamento externo {...} 3>&1 significa que fd 3 é aberto e aponta para onde fd 1 está apontando, i. e. para stdout.

Leitura adicional:

por 12.02.2014 / 14:24