Para esclarecer - você deseja que o stderr vá para a tela e também para o arquivo?
Charles Duffy
Eu fiz, vou editar meu post para esclarecer isso. Acredito que a solução de lhunath será suficiente. Obrigado pela ajuda a todos!
28410 jparanich
Respostas:
785
Suponho que você ainda queira ver STDERR e STDOUT no terminal. Você poderia procurar a resposta de Josh Kelley, mas acho que manter um ambiente tailem segundo plano gera um arquivo muito impreciso e desleixado. Observe como você precisa manter um exra FD e fazer a limpeza posteriormente, matando-o e tecnicamente deve fazer isso em a trap '...' EXIT.
Há uma maneira melhor de fazer isso, e você já descobriu que: tee.
Somente, em vez de apenas usá-lo para o seu stdout, use um tee para stdout e outro para stderr. Como você vai conseguir isso? Substituição de processo e redirecionamento de arquivo:
command >>(tee -a stdout.log)2>>(tee -a stderr.log >&2)
Vamos dividir e explicar:
>>(..)
>(...)(substituição de processo) cria um FIFO e permite teeouvi-lo. Em seguida, ele usa >(redirecionamento de arquivo) para redirecionar o STDOUT do commandpara o FIFO que seu primeiro teeestá ouvindo.
A mesma coisa para o segundo:
2>>(tee -a stderr.log >&2)
Usamos a substituição de processo novamente para fazer um teeprocesso que lê de STDIN e o despeja stderr.log. teeenvia sua entrada de volta ao STDOUT, mas como a entrada é o nosso STDERR, queremos redirecionar teeo STDOUT para o nosso STDERR novamente. Em seguida, usamos o redirecionamento de arquivo para redirecionar commando STDERR do para a entrada do FIFO ( teeo STDIN).
A substituição de processo é uma daquelas coisas realmente adoráveis que você recebe como um bônus de escolher bashcomo seu shell em oposição a sh(POSIX ou Bourne).
Em sh, você teria que fazer as coisas manualmente:
out="${TMPDIR:-/tmp}/out.$$" err="${TMPDIR:-/tmp}/err.$$"
mkfifo "$out""$err"
trap 'rm "$out" "$err"' EXIT
tee -a stdout.log <"$out"&
tee -a stderr.log <"$err">&2&
command >"$out"2>"$err"
Eu tentei isso: o $ echo "HANG" > >(tee stdout.log) 2> >(tee stderr.log >&2)que funciona, mas aguarda a entrada. Existe uma razão simples para isso acontecer?
23711 Justin
1
@SillyFreak Eu não entendo o que você quer fazer ou qual é o problema que você está tendo. teste de eco; exit não produz nenhuma saída no stdout, portanto, o erro permanecerá vazio.
Lhunath
1
obrigado por esse comentário; Eu descobri qual era o meu erro lógico depois: quando invocado como um shell interativo, o bash imprime um prompt de comando e ecoa a saída para o stderr. No entanto, se stderr for redirecionado, o bash será iniciado como não interativo por padrão; Compare /bin/bash 2> erre/bin/bash -i 2> err
Silly Freak
15
E para quem "ver é crer", um teste rápido:(echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
Isso simplesmente redireciona stderrpara stdout, para que o eco seja registrado e exibido. Talvez esteja faltando alguma coisa, porque algumas das outras soluções parecem realmente complicadas.
Nota: Desde a versão 4 do bash, você pode usar |&como uma abreviação para 2>&1 |:
Isso funciona bem se você deseja que stdout (canal 1) e stderr (canal 2) sejam registrados no mesmo arquivo (um único arquivo contendo a mistura de stdout e sterr). A outra solução mais complicada permite separar stdout e stderr em 2 arquivos diferentes (stdout.log e stderr.log, respectivamente). Às vezes isso é importante, às vezes não é.
Tyler Rick
19
As outras soluções são muito mais complicadas do que o necessário em muitos casos. Este funciona perfeitamente para mim.
dkamins
13
O problema com esse método é que você perde o código de saída / status do processo aaa.sh, o que pode ser importante (por exemplo, ao usar em um makefile). Você não tem esse problema com a resposta aceita.
Stefaan
9
se você não se importa com o stdout / stderr mesclado, ele ./aaa.sh |& tee aaa.logfunciona (no bash).
jfs
5
@ Stefaan Eu acredito que você pode manter o status de saída se você preceder a cadeia de comando com set -o pipefailseguido por ;ou &&se não me engano.
David
59
Isso pode ser útil para pessoas que encontram isso no google. Simplesmente remova o comentário do exemplo que você deseja experimentar. Obviamente, fique à vontade para renomear os arquivos de saída.
#!/bin/bash
STATUSFILE=x.out
LOGFILE=x.log
### All output to screen### Do nothing, this is the default### All Output to one file, nothing to the screen#exec > ${LOGFILE} 2>&1### All output to one file and all output to the screen#exec > >(tee ${LOGFILE}) 2>&1### All output to one file, STDOUT to the screen#exec > >(tee -a ${LOGFILE}) 2> >(tee -a ${LOGFILE} >/dev/null)### All output to one file, STDERR to the screen### Note you need both of these lines for this to work#exec 3>&1#exec > >(tee -a ${LOGFILE} >/dev/null) 2> >(tee -a ${LOGFILE} >&3)### STDOUT to STATUSFILE, stderr to LOGFILE, nothing to the screen#exec > ${STATUSFILE} 2>${LOGFILE}### STDOUT to STATUSFILE, stderr to LOGFILE and all output to the screen#exec > >(tee ${STATUSFILE}) 2> >(tee ${LOGFILE} >&2)### STDOUT to STATUSFILE and screen, STDERR to LOGFILE#exec > >(tee ${STATUSFILE}) 2>${LOGFILE}### STDOUT to STATUSFILE, STDERR to LOGFILE and screen#exec > ${STATUSFILE} 2> >(tee ${LOGFILE} >&2)
echo "This is a test"
ls -l sdgshgswogswghthb_this_file_will_not_exist_so_we_get_output_to_stderr_aronkjegralhfaff
ls -l ${0}
Não, e acho que o executivo pode usar algumas explicações. exec >significa mover o destino de um descritor de arquivo para um determinado destino. O padrão é 1, portanto, exec > /dev/nullmove a saída de stdout para / dev / null a partir de agora nesta sessão. Os descritores de arquivo atuais para esta sessão podem ser vistos fazendo ls -l /dev/fd/. Tente! Em seguida, veja o que acontece ao emitir exec 2>/tmp/stderr.log.Além disso, exec 3>&1significa criar um novo descritor de arquivo com o número 3 e redirecioná-lo para o destino do descritor de arquivo 1. No exemplo, o destino era a tela quando o comando foi emitido.
drumfire
O exemplo para mostrar stdout e stderr na tela e em arquivos separados é incrível !!! Muito obrigado!
Marcello de Sales
21
Para redirecionar o stderr para um arquivo, exiba stdout na tela e também salve o stdout em um arquivo:
./aaa.sh 2> ccc.out | tee ./bbb.out
EDIT : Para exibir stderr e stdout na tela e também salvá-los em um arquivo, você pode usar o redirecionamento de E / S do bash :
#!/bin/bash# Create a new file descriptor 4, pointed at the file# which will receive stderr.
exec 4<>ccc.out
# Also print the contents of this file to screen.
tail -f ccc.out &# Run the command; tee stdout as normal, and send stderr# to our file descriptor 4../aaa.sh 2>&4| tee bbb.out
# Clean up: Close file descriptor 4 and kill tail -f.
exec 4>&-
kill %1
Espero que o usuário queira que o stderr vá ao console além do arquivo, embora isso não tenha sido explicitamente especificado.
Charles Duffy
2
Eu deveria ter sido mais claro, eu também queria stderr na tela. Ainda gostei da solução de Josh Kelley, mas acho que a lhunath é mais adequada às minhas necessidades. Obrigado rapazes!
28410 jparanich
18
Em outras palavras, você deseja canalizar stdout em um filtro ( tee bbb.out) e stderr em outro filtro ( tee ccc.out). Não existe uma maneira padrão de canalizar nada além de stdout para outro comando, mas você pode contornar isso manipulando descritores de arquivos.
{{./aaa.sh | tee bbb.out;}2>&11>&3| tee ccc.out;}3>&11>&2
No bash (e ksh e zsh), mas não em outros shells POSIX, como o dash, você pode usar a substituição de processo :
./aaa.sh >>(tee bbb.out)2>>(tee ccc.out)
Lembre-se de que, no bash, esse comando retorna assim que ./aaa.shtermina, mesmo que os teecomandos ainda sejam executados (ksh e zsh aguardam os subprocessos). Isso pode ser um problema se você fizer algo assim ./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out. Nesse caso, use o manipulador do descritor de arquivo ou o ksh / zsh.
Esta parece ser a única resposta que permite manter os fluxos stdout / stderr como estão (por exemplo, não os mesclando). Doce!
user1338062
A abordagem mais simples para sh, útil para tarefas cron, em que a substituição do processo não está disponível.
Roger Dueck
13
Se estiver usando o bash:
# Redirect standard out and standard error separately% cmd >stdout-redirect 2>stderr-redirect
# Redirect standard error and out together% cmd >stdout-redirect 2>&1# Merge standard error with standard out and pipe% cmd 2>&1|cmd2
No meu caso, um script estava executando o comando enquanto redirecionava stdout e stderr para um arquivo, algo como:
cmd > log 2>&1
Eu precisava atualizá-lo para que, quando houver uma falha, execute algumas ações com base nas mensagens de erro. É claro que eu poderia remover o dup 2>&1e capturar o stderr do script, mas as mensagens de erro não serão inseridas no arquivo de log como referência. Embora a resposta aceita pelo @lhunath deva fazer o mesmo, ele redireciona stdoute stderrpara arquivos diferentes, o que não é o que eu quero, mas me ajudou a encontrar a solução exata que eu precisava:
(cmd 2>>(tee /dev/stderr))> log
Com o acima, log terá uma cópia de ambos stdoute stderre posso capturar stderrdo meu script sem ter que se preocupar stdout.
O seguinte funcionará para o KornShell (ksh), onde a substituição do processo não está disponível,
# create a combined(stdin and stdout) collector
exec 3<> combined.log
# stream stderr instead of stdout to tee, while draining all stdout to the collector./aaa.sh 2>&11>&3| tee -a stderr.log 1>&3# cleanup collector
exec 3>&-
O verdadeiro truque aqui é a sequência da 2>&1 1>&3qual, no nosso caso, redireciona o descritor stderrpara stdoute o redireciona . Neste ponto, o e ainda não está combinado.stdout3stderrstdout
Com efeito, o stderr(as stdin) é passado para teeonde ele faz logon stderr.loge também redireciona para o descritor 3.
E o descritor 3está registrando- combined.logo o tempo todo. Então o combined.logcontém ambos stdoute stderr.
Respostas:
Suponho que você ainda queira ver STDERR e STDOUT no terminal. Você poderia procurar a resposta de Josh Kelley, mas acho que manter um ambiente
tail
em segundo plano gera um arquivo muito impreciso e desleixado. Observe como você precisa manter um exra FD e fazer a limpeza posteriormente, matando-o e tecnicamente deve fazer isso em atrap '...' EXIT
.Há uma maneira melhor de fazer isso, e você já descobriu que:
tee
.Somente, em vez de apenas usá-lo para o seu stdout, use um tee para stdout e outro para stderr. Como você vai conseguir isso? Substituição de processo e redirecionamento de arquivo:
Vamos dividir e explicar:
>(...)
(substituição de processo) cria um FIFO e permitetee
ouvi-lo. Em seguida, ele usa>
(redirecionamento de arquivo) para redirecionar o STDOUT docommand
para o FIFO que seu primeirotee
está ouvindo.A mesma coisa para o segundo:
Usamos a substituição de processo novamente para fazer um
tee
processo que lê de STDIN e o despejastderr.log
.tee
envia sua entrada de volta ao STDOUT, mas como a entrada é o nosso STDERR, queremos redirecionartee
o STDOUT para o nosso STDERR novamente. Em seguida, usamos o redirecionamento de arquivo para redirecionarcommand
o STDERR do para a entrada do FIFO (tee
o STDIN).Consulte http://mywiki.wooledge.org/BashGuide/InputAndOutput
A substituição de processo é uma daquelas coisas realmente adoráveis que você recebe como um bônus de escolher
bash
como seu shell em oposição ash
(POSIX ou Bourne).Em
sh
, você teria que fazer as coisas manualmente:fonte
$ echo "HANG" > >(tee stdout.log) 2> >(tee stderr.log >&2)
que funciona, mas aguarda a entrada. Existe uma razão simples para isso acontecer?/bin/bash 2> err
e/bin/bash -i 2> err
(echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
porque não simplesmente:
Isso simplesmente redireciona
stderr
parastdout
, para que o eco seja registrado e exibido. Talvez esteja faltando alguma coisa, porque algumas das outras soluções parecem realmente complicadas.Nota: Desde a versão 4 do bash, você pode usar
|&
como uma abreviação para2>&1 |
:fonte
./aaa.sh |& tee aaa.log
funciona (no bash).set -o pipefail
seguido por;
ou&&
se não me engano.Isso pode ser útil para pessoas que encontram isso no google. Simplesmente remova o comentário do exemplo que você deseja experimentar. Obviamente, fique à vontade para renomear os arquivos de saída.
fonte
exec >
significa mover o destino de um descritor de arquivo para um determinado destino. O padrão é 1, portanto,exec > /dev/null
move a saída de stdout para / dev / null a partir de agora nesta sessão. Os descritores de arquivo atuais para esta sessão podem ser vistos fazendols -l /dev/fd/
. Tente! Em seguida, veja o que acontece ao emitirexec 2>/tmp/stderr.log.
Além disso,exec 3>&1
significa criar um novo descritor de arquivo com o número 3 e redirecioná-lo para o destino do descritor de arquivo 1. No exemplo, o destino era a tela quando o comando foi emitido.Para redirecionar o stderr para um arquivo, exiba stdout na tela e também salve o stdout em um arquivo:
EDIT : Para exibir stderr e stdout na tela e também salvá-los em um arquivo, você pode usar o redirecionamento de E / S do bash :
fonte
Em outras palavras, você deseja canalizar stdout em um filtro (
tee bbb.out
) e stderr em outro filtro (tee ccc.out
). Não existe uma maneira padrão de canalizar nada além de stdout para outro comando, mas você pode contornar isso manipulando descritores de arquivos.Consulte também Como grep stream de erro padrão (stderr)? e quando você usaria um descritor de arquivo adicional?
No bash (e ksh e zsh), mas não em outros shells POSIX, como o dash, você pode usar a substituição de processo :
Lembre-se de que, no bash, esse comando retorna assim que
./aaa.sh
termina, mesmo que ostee
comandos ainda sejam executados (ksh e zsh aguardam os subprocessos). Isso pode ser um problema se você fizer algo assim./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out
. Nesse caso, use o manipulador do descritor de arquivo ou o ksh / zsh.fonte
sh
, útil para tarefas cron, em que a substituição do processo não está disponível.Se estiver usando o bash:
O crédito (sem responder de cima da minha cabeça) está aqui: http://www.cygwin.com/ml/cygwin/2003-06/msg00772.html
fonte
No meu caso, um script estava executando o comando enquanto redirecionava stdout e stderr para um arquivo, algo como:
Eu precisava atualizá-lo para que, quando houver uma falha, execute algumas ações com base nas mensagens de erro. É claro que eu poderia remover o dup
2>&1
e capturar o stderr do script, mas as mensagens de erro não serão inseridas no arquivo de log como referência. Embora a resposta aceita pelo @lhunath deva fazer o mesmo, ele redirecionastdout
estderr
para arquivos diferentes, o que não é o que eu quero, mas me ajudou a encontrar a solução exata que eu precisava:Com o acima, log terá uma cópia de ambos
stdout
estderr
e posso capturarstderr
do meu script sem ter que se preocuparstdout
.fonte
O seguinte funcionará para o KornShell (ksh), onde a substituição do processo não está disponível,
O verdadeiro truque aqui é a sequência da
2>&1 1>&3
qual, no nosso caso, redireciona o descritorstderr
parastdout
e o redireciona . Neste ponto, o e ainda não está combinado.stdout
3
stderr
stdout
Com efeito, o
stderr
(asstdin
) é passado paratee
onde ele faz logonstderr.log
e também redireciona para o descritor 3.E o descritor
3
está registrando-combined.log
o o tempo todo. Então ocombined.log
contém ambosstdout
estderr
.fonte
Se você estiver usando o zsh , poderá usar vários redirecionamentos para não precisar
tee
:Aqui você está simplesmente redirecionando cada fluxo para si e para o arquivo de destino.
Exemplo completo
Observe que isso requer que a
MULTIOS
opção seja definida (que é o padrão).fonte