
Recentemente me deparei com um problema surpreendente com um inválido /dev/stderr
em um cygwin atualizado que também está presente em uma instalação Debian bem desenvolvida . (Editar: ao contrário do que pensei originalmente, meu sistema Debian não expõe esse erro, mas simplesmente produz a saída desejada. Agora devo supor que este é um bug do cygwin.)
Histórico: 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). Estou executando-os controlados por script e, opcionalmente, quero redirecionar a saída barulhenta da ferramenta 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 estava armazenado em uma variável de ambiente. Se a saída para o terminal (ou algum destino controlado pelo usuário) fosse desejada, o destino DBG_STDERR
seria simplesmente "/dev/stderr", caso contrário, algum nome de arquivo temporário. Uma linha típica de execução de ferramenta seria então 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 do contexto. O shell extra é necessário porque say-something.sh seria na verdade chamado por outro script. O redirecionamento redundante de fd 2 para stderr é a "opção" 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 componentes deste pipeline são necessários, como mostram os experimentos após o exemplo fracassado: Todos eles tiveram sucesso.
- Precisamos do tubo final do stdout
- Precisamos da cópia de stderr para stdout pelo chamador
- Precisamos do shell externo em torno da substituição do comando.
Responder1
O nome /dev/stderr
é realmente válido ao redirecionar para um canal. O que pode não ser possível é abrir o alvo final /dev/stderr
diretamente. Apenas Veja:
$ (echo Testing testing > /dev/stderr) |& cat
Testing testing
O tubo criado por |
ou |&
geralmente é umtubo anônimo; o nome mostradonão corresponde a um objeto no sistema de arquivos. Para ilustrar, 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 o alvo (final) de /dev/stderr
; o nome /dev/stderr
é fornecido em ordemevitarpreocupando-se em descobrir o alvo real.
Responder2
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 que você obtém não é o do shell, mas o do processo readlink). O pipe fica inválido quando o processo é encerrado. Tente usar fifos/pipes nomeados.