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

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

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

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

但是有一些选择性的用户输出,我希望同时显示在控制台和日志文件中。但是因为上面的代码,我不能这样做。

我知道,如果我删除上述代码,我可以得到期望的结果,这种情况下,但我将不得不手动写入日志文件的所有其他输出,这是一个不容易的任务。

Is there a way to get the output in both the console and the log file without removing the above codes?

427763 次浏览

是的,你想使用 tee:

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

Just pipe your command to tee and pass the file as an argument, like so:

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

This both prints the output to the STDOUT and writes the same output to a log file. See man tee for more information.

注意,这不会将 stderr 写入日志文件,因此如果您想合并这两个流,那么可以使用:

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

将把 stdout 和 stderr 输出发送到日志文件中,但是也会将 fd3连接到控制台,因此可以这样做

echo "Some console message" 1>&3

只向控制台写入消息,或者

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

为了向控制台 and写一条消息,日志文件 tee将它的输出发送到它自己的 fd1(这里是 LOG_FILE)和你让它写的文件(这里是 fd3,也就是控制台)。

例如:

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

进入日志文件。

for log file you may date to enter into text data. following code may help

# 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。日志文件将在第一次运行时创建,并在下次运行时继续更新。如果日志文件在将来的运行中丢失,脚本将创建新的日志文件。

I have found a way to get the desired output. Though it may be somewhat unorthodox way. Anyways here it goes. In the redir.env file I have following code:

#####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只输出到控制台,木头只输出到日志文件,信息输出到日志文件和控制台。

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

在控制台

在控制台
回声只是为了安慰
控制台和日志

For the Log file

In Log File 只写入日志文件
这是 stderr,只写入日志文件
控制台和日志

希望这个能帮上忙。

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

exec: 1: not found

这是对我来说最有效的方法(确认也可以在 zsh 中工作) :

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

The file /tmp/both.log afterwards contains

this is stdout
chmmm command not found

除非从 tee 中删除-a,否则将追加/tmp/both.log。

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

我发现将 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-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中似乎不能使用 multios。

我希望在标准输出和日志文件上显示日志以及时间戳。以上的答案对我来说都不管用。 我使用了 工序替代工序替代执行官命令,编写了以下代码。 Sample logs:

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)

希望这对谁有帮助!

试试这个,它会起作用的:

log_file=$curr_dir/log_file.txt
exec > >(tee -a ${log_file} )
exec 2> >(tee -a ${log_file} >&2)
    #
#------------------------------------------------------------------------------
# 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