Pergunta

Eu quero redirecionar ambos stdout e stderr de um processo em um único arquivo. Como posso fazer isso em Bash?

Foi útil?

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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top