web-dev-qa-db-ja.com

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

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

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

Envファイルは各スクリプトの始めに実行されます。 envファイル内の上記のコードのために、ユーザー出力またはエラーである可能性があるすべてのコンソール出力は、私が実際に必要としているものであるログファイルに直接出力されます。

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

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

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

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

標準出力と標準エラー出力をログファイルに送りますが、fd 3をコンソールに接続したままにしておきます。

echo "Some console message" 1>&3

コンソールだけにメッセージを書き込む

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

メッセージをbothコンソールandログファイルに書き込む - teeは、自身のfd 1(ここではLOG_FILEです)とそれに伝えたファイルの両方に出力を送信します。書き込み先(ここではfd 3、つまりコンソールです)。

例:

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

ログファイルに。

85
Ian Roberts

はい、teeを使用します。

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

コマンドをteeにパイプして、ファイルを引数として渡すだけです。

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

これは、出力をSTDOUTに出力し、同じ出力をログファイルに書き込みます。詳細についてはman teeを参照してください。

これはログファイルにstderrを書きませんので、2つのストリームを結合したいのであれば:

exec 1 2>&1 | tee ${LOG_FILE}
36
Jon Cairns

私はjoontyの答えを試してみましたが、私はまた得ました

exec:1:見つかりません

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

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

ファイル/tmp/both.logの後には、

this is stdout
chmmm command not found 

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

ヒント:>(...)はプロセス置換です。ファイルのようにexecteeコマンドに渡すことができます。

29
alfonx

私はタイムスタンプと一緒に標準出力とログファイルにログを表示したいと思いました。上記の答えのどれも私のために働きませんでした。私はプロセス置換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)

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

4
Jyoti Dhiman

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

# 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.ログファイルは最初の実行で作成され、次の実行から更新を続けます。将来の実行でログファイルが見つからない場合は、スクリプトによって新しいログファイルが作成されます。

3
user2197712

希望の出力を得る方法を見つけました。それは多少非正統的な方法かもしれませんが。とにかくここに行きます。 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

ここでechoはコンソールにのみ出力し、logはログファイルにのみ出力し、messageはログファイルとコンソールの両方に出力します。

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

コンソール内

コンソール内
コンソール専用にエコー
コンソールにしてログに記録する

ログファイル用

ログファイル内ログファイルのみに書き込まれる
これは標準エラーです。ログファイルのみに書き込まれます
コンソールにしてログに記録する

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

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
1
Yordan Georgiev

Stdoutとstderrの両方をログファイルに追加すると非常に便利です。私はexecを使ってこれを達成する方法を疑問に思っていたので、私はexec > >(tee -a)でalfonxによる解決策を見て嬉しかったです。 here-doc構文と.を使用したクリエイティブな解決方法に出会いました。 https://unix.stackexchange.com/questions/80707/how-to-output-text-to-both-screenシェルスクリプト内のファイルとファイル

Zshでは、here-docソリューションを "multios"構文を使用して出力を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を省略した場合、スクリプト全体がログ記録付きで実行されます。 zshがどのようにmultiosを実装しているかはわかりませんが、teeよりもオーバーヘッドが少なくなる可能性があります。残念ながら、execでmultiosを使うことはできないようです。

0
Metamorphic

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

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