プロセスのstdoutとstderrの両方を1つのファイルにリダイレクトしたい。バッシュでどうすればいいですか?
プロセスのstdoutとstderrの両方を1つのファイルにリダイレクトしたい。バッシュでどうすればいいですか?
回答:
#!/bin/bashのではなく#!/bin/shbashのを必要であるため、。
                    do_something 2>&1 | tee -a some_fileこれにより、stderrがstdoutおよびstdoutにリダイレクトされ、stdoutに出力さsome_file れます。
do_something &>filenameはそうではありません。+1。
                    Ambiguous output redirect.理由すべてのアイデアを?
                    $?の終了ステータスでdo_somethingはなく、の終了ステータスを参照するという副作用があることに注意してくださいtee。
                    stderrをstdoutにリダイレクトし、stdoutをファイルにリダイレクトできます。
some_command >file.log 2>&1 見る http://tldp.org/LDP/abs/html/io-redirection.htmlを
この形式は、bashでのみ機能する最も一般的な&>形式よりも推奨されます。Bourneシェルでは、コマンドをバックグラウンドで実行していると解釈できます。また、形式はより読みやすく、2(STDERR)は1(STDOUT)にリダイレクトされます。
編集:コメントで指摘されているように順序を変更しました
# 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'"これで、単純なエコーは$ LOG_FILEに書き込みます。デーモン化に役立ちます。
元の投稿の著者に、
それはあなたが達成する必要があることに依存します。スクリプトから呼び出すコマンドの内外にリダイレクトする必要があるだけの場合、回答は既に提供されています。鉱山は現在のスクリプト内でリダイレクトすることについてであり、言及されたコードスニペットの後のすべてのコマンド/組み込み(フォークを含む)に影響します。
別のクールなソリューションは、std-err / outとロガーまたはログファイルの両方に一度にリダイレクトすることです。これには、「ストリーム」を2つに分割することが含まれます。この機能は、複数のファイル記述子(ファイル、ソケット、パイプなど)に一度に書き込み/追加できる「tee」コマンドによって提供されます: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
}それで、最初から。端末が/ dev / stdout(FD#1)と/ dev / stderr(FD#2)に接続されていると仮定しましょう。実際には、パイプ、ソケットなど何でもかまいません。
上記の行に加えて次の行を含むスクリプトを実行した結果:
echo "Will end up in STDOUT(terminal) and /var/log/messages"...以下のとおりであります:
$ ./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より鮮明な画像を表示するには、次の2行をスクリプトに追加します。
ls -l /proc/self/fd/
ps xf奇妙なことに、これはうまくいきます:
yourcommand &> filenameしかし、これは構文エラーになります:
yourcommand &>> filename
syntax error near unexpected token `>'使用する必要があります:
yourcommand 1>> filename 2>&1&>>BASH 4で動作するようです$ echo $BASH_VERSION  4.1.5(1)-release $ (echo to stdout; echo to stderr > /dev/stderr) &>> /dev/null
                    短い答え:Command >filename 2>&1またはCommand &>filename
説明:
「stdout」という単語をstdoutに出力し、「stderror」という単語をstderrorに出力する次のコードを考えてみます。
$ (echo "stdout"; echo "stderror" >&2)
stdout
stderror「&」演算子は、2がファイル記述子ではなく(stderrを指す)ファイル記述子であることをbashに通知することに注意してください。「&」stdoutを省略した場合、このコマンドはstdoutに出力し、「2」という名前のファイルを作成して次のように書き込みます。stderrorそこにます。
上記のコードを試してみると、リダイレクト演算子がどのように機能するかを正確に確認できます。たとえば、次の2行のコードに1,2リダイレクトされる2つの記述子のうちどちらのファイルを変更するかによって/dev/null、stdoutからすべてが、stderrorからすべてが削除されます(残っているものを出力します)。
$ (echo "stdout"; echo "stderror" >&2) 1>/dev/null
stderror
$ (echo "stdout"; echo "stderror" >&2) 2>/dev/null
stdoutここで、次のコードが出力を生成しない理由を説明できます。
(echo "stdout"; echo "stderror" >&2) >/dev/null 2>&1これを本当に理解するには、ファイル記述子テーブルに関するこのWebページを読むことを強くお勧めします。あなたがその読みを終えたとすれば、私たちは先へ進むことができます。Bashは左から右に処理することに注意してください。したがって、Bashは>/dev/null最初に(これはと同じ1>/dev/null)を参照し、ファイル記述子1をstdoutではなく/ dev / nullを指すように設定します。これを行うと、Bashは右方向に移動してを確認し2>&1ます。これにより、ファイル記述子2は、ファイル記述子1 と同じファイルを指すように設定されます(ファイル記述子1自体を指すのではありません!!!!(ポインターについては、このリソースを参照してください)詳細については)))。ファイル記述子1は/ dev / nullを指し、ファイル記述子2はファイル記述子1と同じファイルを指すため、ファイル記述子2も/ dev / nullを指すようになります。したがって、両方のファイル記述子が/ dev / nullを指しているため、出力がレンダリングされません。
概念を本当に理解しているかどうかをテストするには、リダイレクトの順序を切り替えたときの出力を推測してみてください。
(echo "stdout"; echo "stderror" >&2)  2>&1 >/dev/nullstderror
ここでの推論は、左から右に評価すると、Bashは2>&1を参照するため、ファイル記述子2がファイル記述子1と同じ場所、つまりstdoutを指すように設定するためです。次に、ファイル記述子1を設定し(> / dev / null = 1> / dev / null)、> / dev / nullを指すように設定して、通常は標準出力に送信されるものをすべて削除します。したがって、残っているのは、サブシェル(括弧内のコード)のstdoutに送信されなかったもの、つまり「stderror」だけです。1が単なるstdoutへのポインタであっても、ポインタ2を1にリダイレクトしても、ポインタ2>&1のチェーン2-> 1-> stdoutは形成されないことに注意してください。存在する場合、1を/ dev / nullにリダイレクトした結果、コード2>&1 >/dev/null ポインタチェーン2-> 1-> / dev / nullを与えるため、上記のコードとは対照的に、コードは何も生成しません。 
最後に、これを行う簡単な方法があることに注意します。
ここ 3.6.4項から、演算子&>を使用してstdoutとstderrの両方をリダイレクトできることがわかります。したがって、コマンドのstderrとstdoutの両方の出力を\dev\null(出力を削除する)にリダイレクトするには、単に入力する 
 $ command &> /dev/null 
か、私の例の場合は次のようにします。
$ (echo "stdout"; echo "stderror" >&2) &>/dev/null重要なポイント:
2>&1 >/dev/null!= >/dev/null 2>&1です。1つは出力を生成し、もう1つは生成しません。最後に、次のすばらしいリソースをご覧ください。
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" )これは関連しています:syslogへのstdOut&stderrの書き込み。
ほとんど機能しますが、xintedからは機能しません;(
stdoutとstderrからの出力をログファイルに書き込み、stderrをコンソールに残すためのソリューションが必要でした。だから私はティーを介してstderr出力を複製する必要がありました。
これは私が見つけた解決策です:
command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile状況については、「パイピング」が必要なときに使用できます。
|&
例えば:
echo -ne "15\n100\n"|sort -c |& tee >sort_result.txtまたは
TIMEFORMAT=%R;for i in `seq 1 20` ; do time kubectl get pods |grep node >>js.log  ; done |& sort -hこのbashベースのソリューションは、STDOUTとSTDERRを別々にパイプできます(「sort -c」のSTDERRから、またはSTDERRから「sort -h」へ)。
次の関数を使用して、stdout / stderrとログファイルの出力を切り替えるプロセスを自動化できます。
#!/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"
    }スクリプト内での使用例:
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"@ fernando-fabreti
あなたがしたことに加えて、私は関数を少し変更して&-クロージングを削除しましたが、それは私にとってはうまくいきました。
    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"exec 2>&1私のようなものを使用することを検討する状況では、可能であれば、次のようなbash関数を使用してコードを書き直すと読みやすくなります。
function myfunc(){
  [...]
}
myfunc &>mylog.log