Por que o / dev / stderr é inválido ao redirecionar para um pipe no cygwin?

0

Eu tropecei recentemente em um problema surpreendente com um /dev/stderr inválido em um cygwin atualizado, que também está presente em uma instalação Debian bem desenvolvida . (Edit: Em oposição ao que eu pensava originalmente, meu sistema Debian não expõe este erro, mas simplesmente produz a saída desejada. Eu agora devo supor que isso é um bug do cygwin.)

Antecedentes: estou usando ferramentas que produzem milhares de linhas de saída (especificamente: sistemas de controle de versão em um grande sistema de produção). Eu estou executando-los controlado por script e queria, opcionalmente, redirecionar a saída da ferramenta barulhenta para um arquivo de log. Uma solução simples parecia sempre redirecionar sua saída (stderr e stdout) para um destino do sistema de arquivos que era armazenado em uma variável de ambiente. Se a saída para o terminal (ou algum destino controlado pelo usuário) for desejada, o destino DBG_STDERR seria simplesmente "/ dev / stderr", caso contrário, algum nome de arquivo temporário. Uma linha de execução de ferramenta típica seria semelhante a noisy_command >> "$DBG_STDERR" 2>&1 .

Isso funciona bem, a menos que eu canalize a saída do script. Aqui está uma reprodução mínima:

$ uname -a
CYGWIN_NT-6.1-WOW xxxxxxx 2.8.1(0.312/5/3) 2017-07-03 14:06 i686 Cygwin

$ bash --version
GNU bash, version 4.4.12(3)-release (i686-pc-cygwin)

$ cat say-something.sh
#!/bin/sh
echo something > /dev/stderr

$ (x=$(./say-something.sh 2> /dev/stderr)) 2>&1 |cat
./say-something.sh: line 2: /dev/stderr: No such file or directory

$ (x=$(./say-something.sh 2> /dev/stderr)) 2>&1
something

$ (x=$(./say-something.sh 2> /dev/stderr))  |cat
something

$ x=$(./say-something.sh 2> /dev/stderr) 2>&1 |cat
something

É claro que todos os redirecionamentos e shells aninhados parecem engraçados fora de contexto. O shell extra é necessário porque say-something.sh seria realmente chamado por outro script. O redirecionamento redundante de fd 2 para stderr é o "switch" para facilitar o redirecionamento opcional para um arquivo (/ dev / stderr, ou um caminho diferente, é na verdade o conteúdo configurável de uma variável).

Parece que todos os constituintes desse pipeline são necessários, como mostram os experimentos após o exemplo fracassado: todos são bem-sucedidos.

  • Precisamos do tubo final de stdout
  • Precisamos da cópia do stderr para stdout pelo chamador
  • Precisamos do shell externo em torno da substituição de comandos.
por Peter A. Schneider 06.07.2017 / 16:16

2 respostas

4

O nome /dev/stderr é realmente válido ao redirecionar para um pipe. O que pode não ser possível é abrir a meta final de /dev/stderr diretamente. Basta ver:

$ (echo Testing testing > /dev/stderr) |& cat
Testing testing

O pipe criado por | ou |& é geralmente um pipe anônimo ; o nome mostrado não corresponde a um objeto no sistema de arquivos . Para uma ilustração você pode tentar algo simples como:

$ ls -la /dev/fd/ |& cat
total 0
dr-x------ 2 alexp alexp  0 Jul  6 18:23 .
dr-xr-xr-x 9 alexp alexp  0 Jul  6 18:23 ..
lrwx------ 1 alexp alexp 64 Jul  6 18:23 0 -> /dev/pts/4
l-wx------ 1 alexp alexp 64 Jul  6 18:23 1 -> pipe:[1058859]
l-wx------ 1 alexp alexp 64 Jul  6 18:23 2 -> pipe:[1058859]
lr-x------ 1 alexp alexp 64 Jul  6 18:23 3 -> /proc/4335/fd

É muito incomum tentar abrir a meta (final) de /dev/stderr ; o nome /dev/stderr é fornecido na ordem para evitar incomodar-se em descobrir o alvo real.

    
por 06.07.2017 / 17:31
1

Eu acho que o problema é o processo readline que é gerado. Ele obtém seu próprio canal para o redirecionamento que é fechado quando o processo é interrompido (o pid obtido não é o do shell, mas o do processo readlink). O pipe fica inválido quando o processo sai. Tente usar fifos / named pipes.

    
por 06.07.2017 / 17:36