Como redirecionar stdout e stderr para um arquivo e exibir stderr para o console?

18

Eu sei como redirecionar para um arquivo e usar tee; em um nível básico. então

$ alias outanderr='bash -c "echo stdout >&1; echo stderr >&2"'
# A fake "application" displaying both output and error messages.

$ outanderr 1>file      # redirect stdout to a file, display stderr
stderr

$ outanderr 2>file      # redirect stderr to a file, display stdout
stdout

$ outanderr 1>file 2>&1 # redirect both to a file, display nothing

$ outanderr | tee file; echo "-- file contents --" && cat file
# redirect stdout to a file, display both (note: order is messed up)
stderr
stdout
-- file contents --
stdout

$ outanderr 2>&1 | tee file; echo "-- file contents --" && cat file
# redirect both to a file, display both
stdout
stderr
-- file contents --
stdout
stderr

A questão é: o que escrever no lugar dos pontos de interrogação para obter a saída abaixo:

$ outanderr ???; echo "-- file contents --" && cat file
# redirect both to a file, display stderr
stderr
-- file contents --
stdout
stderr

Constaints:

  • Assumindo festança.
  • A ordem deve ser mantida no arquivo.
  • O conteúdo do stderr é exibido em tempo real, linha por linha, ou seja, sem buffer.
  • Arquivos de script separados podem ser usados.
  • Magia pode ser necessária.
TWiStErRob
fonte
Quanto controle do outanderrprograma você tem?
21713 Kevin
11
@ Kevin Eu acho que a pergunta é mais genérica do que isso. Aqui outanderrestá apenas um alias que imprime uma linha para stdout e outra para stderr. A idéia (se possível) é criar uma solução genérica que possa funcionar com qualquer programa, sem modificá-los.
lgeorget
@lgeorget Entendo isso, mas não acredito que seja possível atender estritamente a todas as restrições em uma solução genérica, então estava vendo se conseguiríamos uma específica.
21413 Kevin
@Igeorget está certo.
TWiStErRob

Respostas:

12
2>&1 >>outputfile | tee --append outputfile

Para testes fáceis:

echo -n >outputfile; bash -c "echo stdout >&1; echo stderr >&2" 2>&1 >>outputfile |
  tee --append outputfile; echo "outputfile:"; cat outputfile

Editar 1:

Isso funciona gravando stdout (somente) no arquivo, tornando sterr stdout para que ele atravesse o pipe e fazendo com que tee grave sua saída no mesmo arquivo.

As duas gravações devem ser feitas no modo de acréscimo (em >>vez de >), caso contrário, ambas substituiriam a saída uma da outra.

Como o pipe é um buffer, não há garantia de que a saída apareça no arquivo na ordem correta. Isso nem mudaria se um aplicativo estivesse conectado aos dois descritores de arquivo (dois pipes). Por ordem garantida, ambas as saídas teriam que passar pelo mesmo canal e ser marcadas respectivamente. Ou você precisaria de algumas coisas realmente chiques:

  1. Se stdout e stderr forem redirecionados para um arquivo (não o mesmo arquivo!) E os dois estiverem em um volume FUSE, o módulo FUSE poderá marcar cada gravação única com um carimbo de data / hora para que um segundo aplicativo possa classificar os dados corretamente e combiná-los para o arquivo de saída real. Ou você não marca os dados, mas solicita que o módulo crie o arquivo de saída combinado. Provavelmente ainda não existe um módulo FUSE que faça isso ...
  2. Tanto stdout quanto stderr podem ser direcionados /dev/null. As saídas do aplicativo seriam separadas executando-o strace -f -s 32000 -e trace=write. Você teria que reverter a fuga nesse caso. Escusado será dizer que o aplicativo não roda mais rápido por ser rastreado.
  3. Talvez o mesmo possa ser alcançado usando um módulo FUSE simples e existente e rastreando o módulo em vez do aplicativo. Isso pode ser mais rápido do que rastrear o aplicativo porque (ou melhor: se) o módulo provavelmente possui muito menos syscalls que o aplicativo.
  4. Se o aplicativo em si puder ser modificado: O aplicativo poderá ser interrompido após cada saída (mas acho que isso é possível apenas a partir do interior) e continuar somente após o recebimento do sinal s (SIGUSR1 ou SIGCONT). A leitura do aplicativo no canal teria que verificar o canal e o arquivo em busca de novos dados e enviar o sinal após cada novo dado. Dependendo do tipo de aplicação, isso pode ser mais rápido ou mais lento que o método strace. O FUSE seria a solução de velocidade máxima.
Hauke ​​Laging
fonte
11
Bah. Me pegue no meio da escrita exatamente a mesma resposta, por que não?
21713 Kevin
2
NB, isso tem uma condição de corrida, introduzindo a possibilidade de trocar / errar linhas, mas não acho que possa ser evitado.
21713 Kevin
11
@ Kevin Isso acontece com os melhores de nós, já sofri com isso antes e quase pedi um recurso "mostre-me que alguém está escrevendo" (o que seria complicado, no entanto). Parece-me que a condição de corrida ocorre apenas se uma gravação no arquivo (stdout) ocorrer após uma gravação no pipeline.
amigos estão dizendo sobre hauke laging
Isso não enviaria ambos stdoute stderrpara tee, ou estou perdendo alguma coisa? Eu acho que o requisito do OP é tee stderrapenas.
Joseph R.
@JosephR. Você não experimentou isso?
amigos estão dizendo sobre hauke laging