ログファイルとコンソールへの出力の書き込み


98

Unixシェルでは、すべての出力(エコーメッセージをリダイレクトするenvファイル(ログファイル名とパス、ログファイルへの出力とエラーのリダイレクト、データベース接続の詳細など)のようなユーザースクリプトを実行するために必要なパラメーターを定義するenvファイルを持っています))および次のコードを使用して実行されたスクリプトからログファイルへのエラー:

exec 1>>${LOG_FILE}
exec 2>>${LOG_FILE}

envファイルは、各スクリプトの最初に実行されます。envファイルの上記のコードにより、ユーザー出力またはエラーである可能性のあるすべてのコンソール出力が、実際に必要なログファイルに直接出力されます。

しかし、コンソールとログファイルの両方に表示したいユーザー出力がいくつかあります。しかし、上記のコードのために、私はそうすることができません。

上記のコードを削除すると、この場合に望ましい結果が得られることはわかっていますが、他のすべての出力を手動でログファイルに書き込む必要がありますが、これは簡単な作業ではありません。

上記のコードを削除せずに、コンソールとログファイルの両方で出力を取得する方法はありますか?

回答:


105
exec 3>&1 1>>${LOG_FILE} 2>&1

stdoutおよびstderr出力をログファイルに送信しますが、fd 3をコンソールに接続したままにするため、次のことができます

echo "Some console message" 1>&3

コンソールにのみメッセージを書き込む、または

echo "Some console and log file message" | tee /dev/fd/3

メッセージをコンソールログファイルの両方に書き込む- 出力を独自のfd 1(これは)と、書き込みを指示したファイル(ここはfd 3、つまりコンソール)の両方に送信します。teeLOG_FILE

例:

exec 3>&1 1>>${LOG_FILE} 2>&1

echo "This is stdout"
echo "This is stderr" 1>&2
echo "This is the console (fd 3)" 1>&3
echo "This is both the log and the console" | tee /dev/fd/3

印刷する

This is the console (fd 3)
This is both the log and the console

コンソールに入れて

This is stdout
This is stderr
This is both the log and the console

ログファイルに。


2
それはあなたが提案したとおりに機能しました。しかし、私は/ dev / fd / 3を理解していませんでした。teeがログファイルとコンソールにメッセージを書き込むことを知っていますが、teeの後に使用される/ dev / fd / 3を正確に理解していませんでした
abinash shrestha

@shresthaティーの珍しい使い方だと思う。/dev/fd/3「現在開いてFD 3」を意味し、ファイル名なので、tee /dev/fd/31をfdにその標準入力に到着したものは何でも書いても3(FDます/dev/fd/3ファイル)。Fd 1はログファイルに接続され、fd 3はコンソールに接続されます。
Ian Roberts

また、コンソールにではなく、ファイルにのみ書き込みたい場合:echo "blah" | tee file1.txt | tee file2.txt >/dev/null 「Blah」はfile1.txtとfile2.txtに書き込まれず、コンソールには書き込まれません。
–danger89

これは私にとって非常に役に立ちました。私はトピックから外れているかもしれない何かに気づきましたが、おそらく何人かはこれの理由を知っています。bashスクリプトからRスクリプトを実行しています。さまざまなR関数のコンソール出力は色分けされています(私が定義したとおり)。上記の方法を使用して出力をコンソールとログファイルにリダイレクトすると、コンソールの出力に色が付きません。その理由は何でしょうか?
dieHellste 2018

1
@dieHellste一部のプログラムでは、出力が別のプロセス(この場合teeは、端末に書き込まれます)にパイプ処理されているときを検出して、端末に直接行くのではなく、それらの出力を一致するように調整できます。
Ian Roberts

43

はい、使用したいtee

tee-標準入力から読み取り、標準出力とファイルに書き込みます

次のように、コマンドをtにパイプし、ファイルを引数として渡します。

exec 1 | tee ${LOG_FILE}
exec 2 | tee ${LOG_FILE}

これにより、出力がSTDOUTに出力され、同じ出力がログファイルに書き込まれます。見るman tee、をしてください。

これはstderrをログファイルに書き込まないため、2つのストリームを結合する場合は、次のコマンドを使用してください。

exec 1 2>&1 | tee ${LOG_FILE}

2
上記の解決策は機能しませんでした。##### redirect.env ###### export LOG_FILE = log.txt exec 1 2>&1 | tee -a $ {LOG_FILE} exec 1 | tee -a $ {LOG_FILE} exec 2 | tee -a $ {LOG_FILE} #########および作業ファイルには次のコードが含まれています:##### output.sh ######!/ bin / sh。redirect.env echo "有効な出力" ech "無効な出力" ##############エラーが表示されます:#### redirect.env:行3:exec:1:見つかりませんredirect.env:5行目:exec:1:見つかりませんredirect.env:6行目:exec:2:見つかりません####とログファイルでも同じエラーが発生します。私は何か悪いことをしていますか?
abinash shrestha

コメントでは新しい行が取り除かれているため、わかりにくいです。次のようなどこかにコードのペーストを追加することができ要旨
ジョンケアンズ

1
ファイルをリンクUnixRedirectに追加しました。関連ファイルは、redirect.envおよびoutput.shです
abinash shrestha

1
このコードは機能していないようです(少なくとももう機能していません)。あなたは通常得るでしょうscript.sh: line 5: exec: 1: not found
tftd

39

joontyの答えを試しましたが、

exec:1:見つかりません

エラー。これは私にとって最もうまくいくものです(zshでも動作することが確認されています):

#!/bin/bash
LOG_FILE=/tmp/both.log
exec > >(tee ${LOG_FILE}) 2>&1
echo "this is stdout"
chmmm 77 /makeError

/tmp/both.logファイルは後で含まれています

this is stdout
chmmm command not found 

Tから-aを削除しない限り、/ tmp / both.logが追加されます。

ヒント:>(...)プロセスの代替です。それexectee、それがファイルであるかのようにコマンドを実行します。


1
これは私にとって魅力のように機能しましたが、他の答えはヒットまたはミスされました。
ジェイテイラー

このスニペットを共有していただきありがとうございます。これは(他の回答と比較して)うまく機能しているようです!
tftd 2016年

これは機能しますが、実行後、私のシェルは異なります。
Josh Usre

何か助けをしたいか、あなたは他のSO-ユーザーを支援したい場合@JoshUsreは、異なっているかを説明し、自分の殻、バージョンを記述してくださいOSなど。
alfonx

1
stdoutとstderrを区別する必要がない場合は、ステートメントをに組み合わせることができますexec > >(tee ${LOG_FILE}) 2>&1
darkdragon

5

stdoutのログとタイムスタンプとともにログファイルを表示したいと思いました。上記の答えはどれもうまくいきませんでした。プロセス置換execコマンドを利用して、次のコードを思いつきました。サンプルログ:

2017-06-21 11:16:41+05:30 Fetching information about files in the directory...

スクリプトの上部に次の行を追加します。

LOG_FILE=script.log
exec > >(while read -r line; do printf '%s %s\n' "$(date --rfc-3339=seconds)" "$line" | tee -a $LOG_FILE; done)
exec 2> >(while read -r line; do printf '%s %s\n' "$(date --rfc-3339=seconds)" "$line" | tee -a $LOG_FILE; done >&2)

これが誰かに役立つことを願っています!


すべてのアプリケーションエラーをログに記録してホストディレクトリにログインすることで、開発に役立ちますか?
Prasad Shinde

3

ログファイルの場合、テキストデータに入力する日付を指定できます。次のコードが役立つかもしれません

# declaring variables

Logfile="logfile.txt"   
MAIL_LOG="Message to print in log file"  
Location="were is u want to store log file"

cd $Location   
if [ -f $Logfile ]  
then   
echo "$MAIL_LOG " >> $Logfile

else        

touch $Logfile   
echo "$MAIL_LOG" >> $Logfile    

fi  

出力:2.ログファイルは最初の実行で作成され、次の実行から更新を続けます。今後の実行でログファイルが見つからない場合、スクリプトは新しいログファイルを作成します。


1

希望する出力を得る方法を見つけました。それはやや正統でない方法かもしれませんが。とにかくここに行く。redir.envファイルには、次のコードがあります。

#####redir.env#####    
export LOG_FILE=log.txt

      exec 2>>${LOG_FILE}

    function log {
     echo "$1">>${LOG_FILE}
    }

    function message {
     echo "$1"
     echo "$1">>${LOG_FILE}
    }

次に、実際のスクリプトには次のコードがあります。

#!/bin/sh 
. redir.env
echo "Echoed to console only"
log "Written to log file only"
message "To console and log"
echo "This is stderr. Written to log file only" 1>&2

ここでは、エコー出力はコンソールのみに、ログ出力はログファイルのみに、メッセージ出力はログファイルとコンソールの両方に出力されます。

上記のスクリプトファイルを実行すると、次の出力が得られます。

コンソールで

コンソールでコンソールに
エコーのみ
コンソールとログへ

ログファイル

ログファイル内の ログファイルに書き込まれるだけで
これが標準エラーです。ログファイルのみに書き込まれ、
コンソールとログへ

この助けを願っています。


1

これを試してください、それは仕事をします:

log_file=$curr_dir/log_file.txt
exec > >(tee -a ${log_file} )
exec 2> >(tee -a ${log_file} >&2)

これexec > >(tee -a ${log_file} )は私のニーズにぴったりです。上記の以前のソリューションでは、スクリプトの一部が中断され、失敗した場合は強制的に終了します。ありがとう
MitchellK

1
    #
    #------------------------------------------------------------------------------
    # echo pass params and print them to a log file and terminal
    # with timestamp and $host_name and $0 PID
    # usage:
    # doLog "INFO some info message"
    # doLog "DEBUG some debug message"
    # doLog "WARN some warning message"
    # doLog "ERROR some really ERROR message"
    # doLog "FATAL some really fatal message"
    #------------------------------------------------------------------------------
    doLog(){
        type_of_msg=$(echo $*|cut -d" " -f1)
        msg=$(echo "$*"|cut -d" " -f2-)
        [[ $type_of_msg == DEBUG ]] && [[ $do_print_debug_msgs -ne 1 ]] && return
        [[ $type_of_msg == INFO ]] && type_of_msg="INFO " # one space for aligning
        [[ $type_of_msg == WARN ]] && type_of_msg="WARN " # as well

        # print to the terminal if we have one
        test -t 1 && echo " [$type_of_msg] `date "+%Y.%m.%d-%H:%M:%S %Z"` [$run_unit][@$host_name] [$$] ""$msg"

        # define default log file none specified in cnf file
        test -z $log_file && \
            mkdir -p $product_instance_dir/dat/log/bash && \
                log_file="$product_instance_dir/dat/log/bash/$run_unit.`date "+%Y%m"`.log"
        echo " [$type_of_msg] `date "+%Y.%m.%d-%H:%M:%S %Z"` [$run_unit][@$host_name] [$$] ""$msg" >> $log_file
    }
    #eof func doLog

0

stdoutとstderrの両方をログファイルに追加すると非常に便利です。をexec > >(tee -a)使用してこれを達成する方法を考えていたので、でalfonxによる解決策を見てうれしかったexecです。here-doc構文を使用して創造的なソリューションに出くわしました. https //unix.stackexchange.com/questions/80707/how-to-output-text-to-both-screen-and-file-inside-a-shell -脚本

zshでは、 "multios"構成を使用してhere-docソリューションを変更し、出力をstdout / stderrとログファイルの両方にコピーできることを発見しました。

#!/bin/zsh
LOG=$0.log
# 8 is an arbitrary number;
# multiple redirects for the same file descriptor 
# triggers "multios"
. 8<<\EOF /dev/fd/8 2>&2 >&1 2>>$LOG >>$LOG
# some commands
date >&2
set -x
echo hi
echo bye
EOF
echo not logged

execソリューションほど読みやすくはありませんが、スクリプトの一部だけをログに記録できるという利点があります。もちろん、EOFを省略すると、スクリプト全体がログを記録して実行されます。どのようにzshmultiosを実装するかはわかりませんが、に比べてオーバーヘッドが少ない可能性がありますtee。残念ながら、でmultiosを使用できないようですexec

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.