[bash] STDOUTとSTDERRの両方を端末とログファイルに送るにはどうすればよいですか?



2 Answers

半年後に近づいて...

私はこれがOPによって求められる "完璧な解決策"だと考えています。

あなたのBashスクリプトの一番上に追加できるライナーは次のとおりです:

exec > >(tee -a $HOME/logfile) 2>&1

その使い方を示す小さなスクリプトがあります:

#!/usr/bin/env bash

exec > >(tee -a $HOME/logfile) 2>&1

# Test redirection of STDOUT
echo test_stdout

# Test redirection of STDERR
ls test_stderr___this_file_does_not_exist

(注:これはBashでのみ動作し、/ bin / sh では動作しません )。

hereから適応。 オリジナルは、私が知ることができるものから、ログファイルでSTDERRを捕まえていませんでした。 hereからのノートで修正されhere

Question

私は非技術的なユーザーによって対話的に実行されるスクリプトを持っています。 スクリプトはステータス更新をSTDOUTに書き込みます。これにより、ユーザーはスクリプトが正常に実行されていることを確認できます。

STDOUTとSTDERRの両方を端末にリダイレクトして、(問題があるかどうかを確認するだけでなく、スクリプトも正常に動作していることをユーザーに見せてほしい)。 また、両方のストリームをログファイルにリダイレクトする必要があります。

私はネット上でたくさんのソリューションを見てきました。 いくつかはうまくいかず、他はひどく複雑です。 私は実用的な解決法(答えとして入力する)を開発しましたが、それはクルージングです。

完璧なソリューションは、両方のストリームを端末とログファイルの両方に送信するスクリプトの先頭に組み込むことができる1行のコードです。

EDIT: STDERRをSTDOUTにリダイレクトし、結果をteeに配管しますが、出力をリダイレクトしてパイプすることを覚えているユーザーに依存します。 私はロギングを偽造防止と自動化したいと思っています(これがなぜこのソリューションをスクリプト自体に埋め込むことができるのかということです)。




1年後、ここには何かを記録するための古いbashスクリプトがあります。 例えば、
teelog make ...は、生成されたログ名にログを記録します(ネストされたmakeも記録するトリックを参照してください)。

#!/bin/bash
me=teelog
Version="2008-10-9 oct denis-bz"

Help() {
cat <<!

    $me anycommand args ...

logs the output of "anycommand ..." as well as displaying it on the screen,
by running
    anycommand args ... 2>&1 | tee `day`-command-args.log

That is, stdout and stderr go to both the screen, and to a log file.
(The Unix "tee" command is named after "T" pipe fittings, 1 in -> 2 out;
see http://en.wikipedia.org/wiki/Tee_(command) ).

The default log file name is made up from "command" and all the "args":
    $me cmd -opt dir/file  logs to `day`-cmd--opt-file.log .
To log to xx.log instead, either export log=xx.log or
    $me log=xx.log cmd ...
If "logdir" is set, logs are put in that directory, which must exist.
An old xx.log is moved to /tmp/\$USER-xx.log .

The log file has a header like
    # from: command args ...
    # run: date pwd etc.
to show what was run; see "From" in this file.

Called as "Log" (ln -s $me Log), Log anycommand ... logs to a file:
    command args ... > `day`-command-args.log
and tees stderr to both the log file and the terminal -- bash only.

Some commands that prompt for input from the console, such as a password,
don't prompt if they "| tee"; you can only type ahead, carefully.

To log all "make" s, including nested ones like
    cd dir1; \$(MAKE)
    cd dir2; \$(MAKE)
    ...
export MAKE="$me make"

!
  # See also: output logging in screen(1).
    exit 1
}


#-------------------------------------------------------------------------------
# bzutil.sh  denisbz may2008 --

day() {  # 30mar, 3mar
    /bin/date +%e%h  |  tr '[A-Z]' '[a-z]'  |  tr -d ' '
}

edate() {  # 19 May 2008 15:56
    echo `/bin/date "+%e %h %Y %H:%M"`
}

From() {  # header  # from: $*  # run: date pwd ...
    case `uname` in Darwin )
        mac=" mac `sw_vers -productVersion`"
    esac
    cut -c -200 <<!
${comment-#} from: $@
${comment-#} run: `edate`  in $PWD `uname -n` $mac `arch` 

!
    # mac $PWD is pwd -L not -P real
}

    # log name: day-args*.log, change this if you like --
logfilename() {
    log=`day`
    [[ $1 == "sudo" ]]  &&  shift
    for arg
    do
        log="$log-${arg##*/}"  # basename
        (( ${#log} >= 100 ))  &&  break  # max len 100
    done
            # no blanks etc in logfilename please, tr them to "-"
    echo $logdir/` echo "$log".log  |  tr -C '.:+=[:alnum:]_\n' - `
}

#-------------------------------------------------------------------------------
case "$1" in
-v* | --v* )
    echo "$0 version: $Version"
    exit 1 ;;
"" | -* )
    Help
esac

    # scan log= etc --
while [[ $1 == [a-zA-Z_]*=* ]]; do
    export "$1"
    shift
done

: ${logdir=.}
[[ -w $logdir ]] || {
    echo >&2 "error: $me: can't write in logdir $logdir"
    exit 1
    }
: ${log=` logfilename "$@" `}
[[ -f $log ]]  &&
    /bin/mv "$log" "/tmp/$USER-${log##*/}"


case ${0##*/} in  # basename
log | Log )  # both to log, stderr to caller's stderr too --
{
    From "$@"
    "$@"
} > $log  2> >(tee /dev/stderr)  # bash only
    # see http://wooledge.org:8000/BashFAQ 47, stderr to a pipe
;;

* )
#-------------------------------------------------------------------------------
{
    From "$@"  # header: from ... date pwd etc.

    "$@"  2>&1  # run the cmd with stderr and stdout both to the log

} | tee $log
    # mac tee buffers stdout ?

esac



teeプログラムとdup stderrをstdoutに使用してください。

 program 2>&1 | tee > logfile



Related