114

在 Unix shell 中,我有一个 env 文件(env 文件定义了运行用户脚本所需的参数,如日志文件名和路径、将输出和错误重定向到日志文件、数据库连接详细信息等),它重定向所有输出(回显消息) 并使用以下代码从执行的脚本到日志文件的错误:

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

env 文件在每个脚本的开头执行。由于 env 文件中的上述代码,所有可能是用户输出或错误的控制台输出都直接输出到我实际需要的日志文件中。

但是我希望在控制台和日志文件中都显示一些选择性的用户输出。但是由于上面的代码,我无法这样做。

我知道如果我删除上面的代码,我可以获得这种情况下所需的结果,但我必须手动将所有其他输出写入日志文件,这不是一件容易的事。

有没有办法在不删除上述代码的情况下同时在控制台和日志文件中获取输出?

4

9 回答 9

129
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

将消息写入控制台日志文件 -将其输出发送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

到日志文件中。

于 2013-08-27T10:26:09.413 回答
46

我尝试了joonty的答案,但我也得到了

执行: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 

除非您从 tee 中删除 -a,否则会附加 /tmp/both.log。

提示:>(...)是一个进程替换。它让exec命令tee就像一个文件一样。

于 2014-03-13T09:07:31.947 回答
45

是的,您想使用tee

tee - 从标准输入读取并写入标准输出和文件

只需将您的命令通过管道传递给 tee 并将文件作为参数传递,如下所示:

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

这既将输出打印到 STDOUT,又将相同的输出写入日志文件。有关man tee更多信息,请参阅。

请注意,这不会将 stderr 写入日志文件,因此如果要合并两个流,请使用:

exec 1 2>&1 | tee ${LOG_FILE}
于 2013-08-27T08:09:10.417 回答
6

我想在标准输出和日志文件上显示日志以及时间戳。以上答案都不适合我。我利用了进程替换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)

希望这对某人有帮助!

于 2017-06-21T05:57:00.957 回答
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. 日志文件将在第一次运行时创建,并在下次运行时继续更新。如果以后运行日志文件丢失,脚本将创建新的日志文件。

于 2013-08-27T10:47:16.287 回答
3

试试这个,它会做的工作:

log_file=$curr_dir/log_file.txt
exec > >(tee -a ${log_file} )
exec 2> >(tee -a ${log_file} >&2)
于 2017-07-06T23:07:07.943 回答
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

这里echo只输出到控制台,日志输出只到日志文件,消息输出到日志文件和控制台。

执行上述脚本文件后,我有以下输出:

在控制台中

在控制台
中 仅回显
到控制台 控制台和日志

对于日志文件

在日志 文件中 仅写入日志文件
这是标准错误。仅写入日志文件
控制台和日志

希望这有帮助。

于 2013-08-30T05:17:17.087 回答
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
于 2017-07-25T08:47:29.457 回答
0

我发现将 stdout 和 stderr 都附加到日志文件非常有用。我很高兴看到 alfonx 的解决方案exec > >(tee -a),因为我想知道如何使用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,那么整个脚本都会通过日志记录执行。我不确定如何zsh实现 multios,但它的开销可能比tee. 不幸的是,似乎不能将 multios 与exec.

于 2015-07-29T21:35:42.110 回答