将输出写入日志文件和控制台

时间:2013-08-27 08:06:18

标签: bash shell redirect logging

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

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

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

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

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

有没有办法在控制台和日志文件中获取输出而不删除上述代码?

9 个答案:

答案 0 :(得分:84)

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(这里是{ {1}})和你告诉它写的文件(这里是fd 3,即控制台)。

示例:

LOG_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

进入日志文件。

答案 1 :(得分:36)

是的,您想使用tee

  

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

只需将命令传递给tee并将文件作为参数传递,如下所示:

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

这两者都将输出打印到STDOUT并将相同的输出写入日志文件。有关详细信息,请参阅man tee

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

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

答案 2 :(得分:28)

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

  

exec:1:未找到

错误。这对我来说最有效(confirmed也适用于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。

提示:>(...)是流程替代。它允许exec tee命令,就好像它是一个文件一样。

答案 3 :(得分:4)

我想在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)

希望这有助于某人!

答案 4 :(得分: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。首次运行时将创建日志文件,并在下次运行时继续更新。如果将来运行中缺少日志文件,脚本将创建新的日志文件。

答案 5 :(得分: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 仅输出到控制台,日志输出到日志文件和消息输出到日志文件和控制台。

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

在控制台

  

在控制台中
  仅回应控制台   控制和记录

对于日志文件

  

在日志文件中   只写日志文件
  这是stderr。只写日志文件
  控制和记录

希望得到这个帮助。

答案 6 :(得分: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

答案 7 :(得分: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-script

找到了创意解决方案

我发现在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如何实现多声道,但它的开销可能比tee少。不幸的是,似乎不能使用带有exec的多维数据集。

答案 8 :(得分:0)

试试这个,它会完成工作:

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