stderr redirecionamento e stdout em Bash
Pergunta
Eu quero redirecionar ambos stdout e stderr de um processo em um único arquivo. Como posso fazer isso em Bash?
Solução
Dê uma olhada aqui . Deve ser:
yourcommand &>filename
(redireciona ambos stdout
e stderr
para filename).
Outras dicas
do_something 2>&1 | tee -a some_file
Este vai redirecionamento stderr para stdout e stdout para some_file
e imprimi-lo na saída padrão.
Você pode redirecionar stderr e stdout e stdout em um arquivo:
some_command >file.log 2>&1
http://tldp.org/LDP/abs/html/io -redirection.html
Este formato é o preferido do que o formato mais popular e> que só o trabalho em bash. Em Bourne shell que poderia ser interpretado como executar o comando em background. Além disso, o formato é mais legível 2 (é stderr) redireccionado para um (stdOut).
EDIT: mudou a ordem que fora apontado nos comentários
# Close STDOUT file descriptor
exec 1<&-
# Close STDERR FD
exec 2<&-
# Open STDOUT as $LOG_FILE file for read and write.
exec 1<>$LOG_FILE
# Redirect STDERR to STDOUT
exec 2>&1
echo "This line will appear in $LOG_FILE, not 'on screen'"
Agora, simples echo vai escrever para US $ LOG_FILE. Útil para daemonizing.
Para o autor do post original,
Depende do que você precisa para alcançar. Se você só precisa redirecionar in / out de um comando que você chamar de seu roteiro, as respostas já estão dadas. O meu é sobre redirecionando em script atual que afeta todos os comandos / built-ins (inclui garfos) após o trecho de código mencionado.
Outra solução legal é sobre redirecionando para ambos std-err / out E para logger ou log de arquivos ao mesmo tempo que envolve divisão "um fluxo" em dois. Esta funcionalidade é fornecida pelo comando 'tee' que pode escrever / acréscimo para vários descritores de arquivos (arquivos, tomadas, canos, etc.) de uma só vez: tee FILE1 FILE2 ...> (cmd1)> (cmd2) ...
exec 3>&1 4>&2 1> >(tee >(logger -i -t 'my_script_tag') >&3) 2> >(tee >(logger -i -t 'my_script_tag') >&4)
trap 'cleanup' INT QUIT TERM EXIT
get_pids_of_ppid() {
local ppid="$1"
RETVAL=''
local pids=`ps x -o pid,ppid | awk "\\$2 == \\"$ppid\\" { print \\$1 }"`
RETVAL="$pids"
}
# Needed to kill processes running in background
cleanup() {
local current_pid element
local pids=( "$$" )
running_pids=("${pids[@]}")
while :; do
current_pid="${running_pids[0]}"
[ -z "$current_pid" ] && break
running_pids=("${running_pids[@]:1}")
get_pids_of_ppid $current_pid
local new_pids="$RETVAL"
[ -z "$new_pids" ] && continue
for element in $new_pids; do
running_pids+=("$element")
pids=("$element" "${pids[@]}")
done
done
kill ${pids[@]} 2>/dev/null
}
Assim, desde o início. Vamos supor que temos terminal conectado ao / dev / stdout (FD # 1) e / dev / stderr (FD # 2). Na prática, isso poderia ser um tubo, soquete ou o que quer.
- Criar DFs # 3 e # 4 e apontam para o mesmo "local" como # 1 e # 2, respectivamente. Alterar FD # 1 não afeta FD # 3 de agora em diante. Agora, DFs # 3 e # 4 pontos para STDOUT e STDERR respectivamente. Estes serão usados ??como real STDOUT terminal e STDERR.
- 1>> (...) redireciona STDOUT de comando em parênteses
- parens (sub-shell) executa 'tee' a leitura de STDOUT do exec (pipe) e redirecionamentos de comando 'logger' através de outro tubo para sub-shell em parênteses. Ao mesmo tempo, ele copia a mesma entrada para FD # 3 (terminal)
- Na segunda parte, muito semelhante, é sobre fazer o mesmo truque para STDERR e DFs # 2 e # 4.
O resultado da execução de um script com a linha acima e além disso esta:
echo "Will end up in STDOUT(terminal) and /var/log/messages"
... é o seguinte:
$ ./my_script
Will end up in STDOUT(terminal) and /var/log/messages
$ tail -n1 /var/log/messages
Sep 23 15:54:03 wks056 my_script_tag[11644]: Will end up in STDOUT(terminal) and /var/log/messages
Se você quiser ver quadro mais claro, adicionar estas 2 linhas ao script:
ls -l /proc/self/fd/
ps xf
bash your_script.sh 1>file.log 2>&1
1>file.log
instrui o shell para enviar STDOUT ao file.log
arquivo e 2>&1
diz-lhe para redirecionar STDERR (descritor de arquivo 2) para STDOUT (descritor de arquivo 1).
Nota:. As matérias de ordem como liw.fi apontou, 2>&1 1>file.log
não trabalho
Curiosamente, isso funciona:
yourcommand &> filename
Mas isso dá um erro de sintaxe:
yourcommand &>> filename
syntax error near unexpected token `>'
Você tem que usar:
yourcommand 1>> filename 2>&1
Resposta curta: Command >filename 2>&1
ou Command &>filename
Explicação:
Considere o seguinte código que imprime a palavra "stdout" para stdout ea palavra "stderror" para stderror.
$ (echo "stdout"; echo "stderror" >&2)
stdout
stderror
Note que o '&' operador diz bash que 2 é um descritor de arquivo (que aponta para o stderr) e não um nome de arquivo. Se deixou de fora o '&', este comando iria imprimir stdout
para stdout, e criar um arquivo chamado "2" e escrever stderror
lá.
Através de experiências com o código acima, você pode ver por si mesmo exatamente como operadores de redirecionamento de trabalho. Por exemplo, alterando qual arquivo que do 1,2
dois descritores, é redirecionado para /dev/null
as seguintes duas linhas de código tudo exclusão do stdout, e tudo, desde stderror respectivamente (imprimindo o que resta).
$ (echo "stdout"; echo "stderror" >&2) 1>/dev/null
stderror
$ (echo "stdout"; echo "stderror" >&2) 2>/dev/null
stdout
Agora, podemos explicar por que a solução porque o seguinte código não produz nenhuma saída:
(echo "stdout"; echo "stderror" >&2) >/dev/null 2>&1
Para realmente entender isso, eu recomendo que você leia este webpage em mesas de descritor de arquivo . Supondo que você tenha feito que a leitura, podemos prosseguir. Note-se que Bash processos esquerda para a direita; assim Bash vê >/dev/null
primeiro (que é o mesmo que 1>/dev/null
), e define o descritor de arquivo 1 para apontar para / dev / null em vez da saída padrão. Tendo feito isso, Bash então se move para a direita e vê 2>&1
. Isso define o descritor de arquivo 2 para apontar para o mesmo arquivo como descritor de arquivo 1 (e não ao descritor de arquivo 1 si !!!! (ver este recurso on ponteiros para mais informações)). Desde descritor de arquivo 1 aponta para / dev / null, e descritor de arquivo 2 pontos para o mesmo arquivo como descritor de arquivo 1, descritor de arquivo 2 agora também aponta para / dev / null. Assim, ambos os descritores de arquivos apontam para / dev / null, e é por isso nenhuma saída é processado.
Para testar se você realmente entender o conceito, tentar adivinhar a saída quando mudar a ordem de redirecionamento:
(echo "stdout"; echo "stderror" >&2) 2>&1 >/dev/null
stderror
O raciocínio aqui é que a avaliação da esquerda para a direita, Bash vê 2> & 1, e, portanto, define o descritor de arquivo 2 a apontar para o mesmo lugar como descritor de arquivo 1, ou seja stdout. Em seguida, ele define descritor de arquivo 1 (lembre-se que> / dev / null = 1> / dev / null) para apontar para> / dev / null, excluindo, assim, tudo o que normalmente seria enviar para a saída padrão. Assim, todos nós somos deixados com foi que que não foi enviada a stdout no subnível (o código entre parênteses) - ou seja, "stderror".
A coisa interessante a nota lá é que mesmo que 1 é apenas um ponteiro para o stdout, redirecionando ponteiro 2-1 via 2>&1
não forma uma cadeia de ponteiros 2 -> 1 -> stdout. Se assim fosse, como resultado de redirecionar 1 a / dev / null, o 2>&1 >/dev/null
código daria a cadeia ponteiro 2 -> 1 -> / dev / null, e, portanto, o código poderia gerar nada, em contraste com o que vimos acima .
Finalmente, eu notar que existe uma maneira mais simples de fazer isso:
Na seção 3.6.4 aqui , vemos que podemos usar o &>
operador para redirecionar ambos stdout e stderr. Assim, tanto para redireccionar o stderr e saída stdout de qualquer comando para \dev\null
(que exclui a saída), que simplesmente digitar
$ command &> /dev/null
ou em caso de meu exemplo:
$ (echo "stdout"; echo "stderror" >&2) &>/dev/null
takeaways chave:
- descritores de arquivos comportam como ponteiros (embora descritores de arquivos não são os mesmos como ponteiros de arquivo)
- Redirecionando um descritor de arquivo "a" para um descritor de arquivo "b", que aponta para arquivo "f", provoca descritor de arquivo "a" para apontar para o mesmo place como descritor de arquivo b - arquivo de "f". Não formar uma cadeia de ponteiros a -> b -> f
- Por causa dos, questões de ordem acima,
2>&1 >/dev/null
é! =>/dev/null 2>&1
. Uma gera saída e o outro não!
Finalmente ter um olhar para estes grandes recursos:
Bash Documentação sobre redirecionamento , uma explicação de arquivo Tabelas descritor , Introdução ao Ponteiros
LOG_FACILITY="local7.notice"
LOG_TOPIC="my-prog-name"
LOG_TOPIC_OUT="$LOG_TOPIC-out[$$]"
LOG_TOPIC_ERR="$LOG_TOPIC-err[$$]"
exec 3>&1 > >(tee -a /dev/fd/3 | logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_OUT" )
exec 2> >(logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_ERR" )
Ela está relacionada:. Escrita StdOut & stderr para o syslog
Quase trabalho, mas não de xinted; (
Eu queria uma solução para ter a saída de stdout mais Stderr escrito em um arquivo de log e stderr ainda no console. Então eu precisava para duplicar a saída stderr via tee.
Esta é a solução que eu encontrei:
command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile
- Primeira troca stderr e stdout
- em seguida, acrescentar o stdout para o arquivo de log
- tubulação stderr para tee e anexá-lo também para o arquivo de log
Para a situação, quando "piping" é necessário você pode usar:
| &
Por exemplo:
echo -ne "15\n100\n"|sort -c |& tee >sort_result.txt
ou
TIMEFORMAT=%R;for i in `seq 1 20` ; do time kubectl get pods |grep node >>js.log ; done |& sort -h
Este soluções baseadas festança pode canalizar STDOUT e STDERR separadamente (de STDERR de "tipo -c" ou de STDERR para "sort-h").
"mais fáceis" way (bash4 apenas):. ls * 2>&- 1>&-
As seguintes funções podem ser utilizados para automatizar o processo de alternância saídas beetwen stdout / stderr e um arquivo de log.
#!/bin/bash
#set -x
# global vars
OUTPUTS_REDIRECTED="false"
LOGFILE=/dev/stdout
# "private" function used by redirect_outputs_to_logfile()
function save_standard_outputs {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
exit 1;
fi
exec 3>&1
exec 4>&2
trap restore_standard_outputs EXIT
}
# Params: $1 => logfile to write to
function redirect_outputs_to_logfile {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
exit 1;
fi
LOGFILE=$1
if [ -z "$LOGFILE" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"
fi
if [ ! -f $LOGFILE ]; then
touch $LOGFILE
fi
if [ ! -f $LOGFILE ]; then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1
fi
save_standard_outputs
exec 1>>${LOGFILE%.log}.log
exec 2>&1
OUTPUTS_REDIRECTED="true"
}
# "private" function used by save_standard_outputs()
function restore_standard_outputs {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot restore standard outputs because they have NOT been redirected"
exit 1;
fi
exec 1>&- #closes FD 1 (logfile)
exec 2>&- #closes FD 2 (logfile)
exec 2>&4 #restore stderr
exec 1>&3 #restore stdout
OUTPUTS_REDIRECTED="false"
}
Exemplo de script dentro uso:
echo "this goes to stdout"
redirect_outputs_to_logfile /tmp/one.log
echo "this goes to logfile"
restore_standard_outputs
echo "this goes to stdout"
Para tcsh, eu tenho que usar o seguinte comando:
command >& file
Se o uso command &> file
, vai dar erro "Invalid comando nulo".
@ fernando-fabreti
Somando-se o que você fez eu mudei as funções ligeiramente e retirou o & - fechamento e funcionou para mim.
function saveStandardOutputs {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
exec 3>&1
exec 4>&2
trap restoreStandardOutputs EXIT
else
echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
exit 1;
fi
}
# Params: $1 => logfile to write to
function redirectOutputsToLogfile {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
LOGFILE=$1
if [ -z "$LOGFILE" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"
fi
if [ ! -f $LOGFILE ]; then
touch $LOGFILE
fi
if [ ! -f $LOGFILE ]; then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1
fi
saveStandardOutputs
exec 1>>${LOGFILE}
exec 2>&1
OUTPUTS_REDIRECTED="true"
else
echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
exit 1;
fi
}
function restoreStandardOutputs {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
exec 1>&3 #restore stdout
exec 2>&4 #restore stderr
OUTPUTS_REDIRECTED="false"
fi
}
LOGFILE_NAME="tmp/one.log"
OUTPUTS_REDIRECTED="false"
echo "this goes to stdout"
redirectOutputsToLogfile $LOGFILE_NAME
echo "this goes to logfile"
echo "${LOGFILE_NAME}"
restoreStandardOutputs
echo "After restore this goes to stdout"
Em situações quando você considerar o uso de coisas como exec 2>&1
eu acho mais fácil de ler, se possível código de reescrita usando funções festança assim:
function myfunc(){
[...]
}
myfunc &>mylog.log