[英]Redirect stdout and stderr to Function
我需要帮助将系统命令的输出(stdin 和 stdout)发送到 bash 函数,同时仍接受来自参数的输入。 类似于下面的示例。 有人可以指出我正确的道路吗?
LogMsg()
{
DateTime=`date "+%Y/%m/%d %H:%M:%S"`
echo '*****'$DateTime' ('$QMAKESPEC'): '$1 >> "$LogFile"
echo $DateTime' ('$QMAKESPEC'): '$1
}
# Already works
LogMsg "This statement is sent directly"
# Wish I could do this:
# Capture both stdout & stderr of a system function to the logfile
# I do not presume that any of the syntax that follows is good
make 2>&1 >(LogMsg)
为此,您可以使用read
bash 内置:
LogMsg()
{
read IN # This reads a string from stdin and stores it in a variable called IN
DateTime=`date "+%Y/%m/%d %H:%M:%S"`
echo '*****'$DateTime' ('$QMAKESPEC'): '$IN >> "$LogFile"
echo $DateTime' ('$QMAKESPEC'): '$IN
}
然后使用管道:
make 2>&1 | LogMsg
更新:
为了能够使用标准输入或参数作为输入(根据 chepner 的评论),您可以这样做:
LogMsg()
{
if [ -n "$1" ]
then
IN="$1"
else
read IN # This reads a string from stdin and stores it in a variable called IN
fi
DateTime=`date "+%Y/%m/%d %H:%M:%S"`
echo '*****'$DateTime' ('$QMAKESPEC'): '$IN >> "$LogFile"
echo $DateTime' ('$QMAKESPEC'): '$IN
}
这是一个旧线程..但我用它来帮助我编写一个日志函数,该函数还将输出多行命令输出:
# Defines function to grab a time stamp #
get_Time () { Time=$(date +%Y-%m-%d\ %H:%M:%S) ; }
write_Log()
{
get_Time
if [ -n "${1}" ]; then # If it's from a "<message>" then set it
IN="${1}"
echo "${Time} ${IN}" | tee -a ${log_File}
else
while read IN # If it is output from command then loop it
do
echo "${Time} ${IN}" | tee -a ${log_File}
done
fi
}
根据前面的答案,我整理了一些可以使用或不使用日志文件的通用函数,如本文末尾所列。 这些对于更复杂的脚本很方便。 我通常将终端窗口消息打印到stderr
,以免干扰可能需要重定向的合法程序输出。 函数可以如下调用:
scriptFolder=$(cd $(dirname "$0") && pwd)
scriptName=$(basename $scriptFolder)
# Start a log file that will be used by the logging functions
logFileStart ${scriptName} "${scriptFolder)/${scriptName}.log"
# The following logs the message string passed to the function.
# - use a space for empty lines because otherwise the logging function
# will hang waiting for input
logInfo " "
logInfo "Starting to do some work."
# The following will log each 'stdout` and `stderr` line piped to the function.
someOtherProgram 2>&1 | logInfo
功能...
# Echo to stderr
echoStderr() {
# - if necessary, quote the string to be printed
# - redirect stdout from echo to stderr
echo "$@" 1>&2
# Or, use an alternate echo such one that colors textT
# ${echo2} "$@" 1>&2
}
# Print a DEBUG message
# - prints to stderr and optionally appends to log file if ${logFile} is defined globally
# - see logFileStart() to start a log file
# - call with parameters or pipe stdout and stderr to this function: 2>&1 | logDebug
# - print empty lines with a space " " to avoid hanging the program waiting on stdin input
logDebug() {
if [ -n "${1}" ]; then
if [ -n "${logFile}" ]; then
# Are using a log file
echoStderr "[DEBUG] $@" 2>&1 | tee --append $logFile
else
# Are NOT using a log file
echoStderr "[DEBUG] $@"
fi
else
while read inputLine; do
if [ -n "${logFile}" ]; then
# Are using a log file
echoStderr "[DEBUG] ${inputLine}" 2>&1 | tee --append $logFile
else
# Are NOT using a log file
echoStderr "[DEBUG] ${inputLine}"
fi
done
fi
}
# Print an ERROR message
# - prints to stderr and optionally appends to log file if ${logFile} is defined globally
# - see logFileStart() to start a log file
# - call with parameters or pipe stdout and stderr to this function: 2>&1 | logError
# - print empty lines with a space " " to avoid hanging the program waiting on stdin input
logError() {
if [ -n "${1}" ]; then
if [ -n "${logFile}" ]; then
# Are using a log file
echoStderr "[ERROR] $@" 2>&1 | tee --append $logFile
else
# Are NOT using a log file
echoStderr "[ERROR] $@"
fi
else
while read inputLine; do
if [ -n "${logFile}" ]; then
# Are using a log file
echoStderr "[ERROR] ${inputLine}" 2>&1 | tee --append $logFile
else
# Are NOT using a log file
echoStderr "[ERROR] ${inputLine}"
fi
done
fi
}
# Start a new logfile
# - name of program that is being run is the first argument
# - path to the logfile is the second argument
# - echo a line to the log file to (re)start
# - subsequent writes to the file using log*() functions will append
# - the global variable ${logFile} will be set for use by log*() functions
logFileStart() {
local newLogFile now programBeingLogged
programBeingLogged=$1
# Set the global logfile, in case it was not saved
if [ -n "${2}" ]; then
logFile=${2}
else
# Set the logFile to stderr if not specified, so it is handled somehow
logFile=/dev/stderr
fi
now=$(date '+%Y-%m-%d %H:%M:%S')
# Can't use logInfo because it only appends and want to restart the file
echo "Log file for ${programBeingLogged} started at ${now}" > ${logFile}
}
# Print an INFO message
# - prints to stderr and optionally appends to log file if ${logFile} is defined globally
# - see logFileStart() to start a log file
# - call with parameters or pipe stdout and stderr to this function: 2>&1 | logInfo
# - print empty lines with a space " " to avoid hanging the program waiting on stdin input
logInfo() {
if [ -n "${1}" ]; then
if [ -n "${logFile}" ]; then
# Are using a log file
echoStderr "[INFO] $@" 2>&1 | tee --append $logFile
else
# Are NOT using a log file
echoStderr "[INFO] $@"
fi
else
while read inputLine; do
if [ -n "${logFile}" ]; then
# Are using a log file
echoStderr "[INFO] ${inputLine}" 2>&1 | tee --append $logFile
else
# Are NOT using a log file
echoStderr "[INFO] ${inputLine}"
fi
done
fi
}
# Print an WARNING message
# - prints to stderr and optionally appends to log file if ${logFile} is defined globally
# - see logFileStart() to start a log file
# - call with parameters or pipe stdout and stderr to this function: 2>&1 | logWarning
# - print empty lines with a space " " to avoid hanging the program waiting on stdin input
logWarning() {
if [ -n "${1}" ]; then
if [ -n "${logFile}" ]; then
# Are using a log file
echoStderr "[WARNING] $@" 2>&1 | tee --append $logFile
else
# Are NOT using a log file
echoStderr "[WARNING] $@"
fi
else
while read inputLine; do
if [ -n "${logFile}" ]; then
# Are using a log file
echoStderr "[WARNING] ${inputLine}" 2>&1 | tee --append $logFile
else
# Are NOT using a log file
echoStderr "[WARNING] ${inputLine}"
fi
done
fi
}
感谢发布回复的人。 我想出了我的版本,它将为每条消息添加一次时间戳。
#!/bin/bash
CURRENT_PID=$$
PROCESS_NAME=$(basename $0)
LOGFILE=/var/log/backup-monitor.log
function log_message {
if [ -n "$1" ]; then
MESSAGE="$1"
echo -e "$(date -Iseconds)\t$PROCESS_NAME\t$CURRENT_PID\t$MESSAGE" | tee -a $LOGFILE
else
MESSAGE=$(tee)
echo -e "$(date -Iseconds)\t$PROCESS_NAME\t$CURRENT_PID\t$MESSAGE" | tee -a $LOGFILE
fi
}
log_message "Direct arguments are working!!"
echo "stdin also working" | log_message
您可以创建一个函数,该函数可选择将系统命令中的 STDOUT 和 STDERR 写入日志文件,或通过执行以下操作接受将写入日志文件的参数:
_LOG='/some_dir/some_file'
function Log_Msg {
#If no arguments are given to function....
if [ -z "$@" ]; then
#...then take STDOUT/STDERR as input and write to log file
read && echo "$REPLY" | tee -a $_LOG \
else
#Take arguments that were given to function and write that to log file
echo "$@" | tee -a $_LOG
fi
}
#Logging from system commands example. The "|&" operator pipes STDOUT and STDERR to Log_Msg function
bad command |& Log_Msg
or
#Taking an argument as input and writing to log file
Log_Msg "Write this to log file"
希望这可以帮助!
在我看来,读取命令中的 100 毫秒(-t 0.1)超时将允许 LogMsg 处理输入管道和参数,而无需在没有输入的情况下永远等待。
function log(){ read -t 0.1 IN1
echo $(date "+%Y/%m/%d %H:%M:%S")' ('$QMAKESPEC'): '$IN1 $* |tee -a $LogFile ;}
#test without, with pipe , with pipe and parameters , with parameters only
log ; echo foo | log ; echo foo | log bar ; log bar
2015/01/01 16:52:17 ():
2015/01/01 16:52:17 (): foo
2015/01/01 16:52:17 (): foo bar
2015/01/01 16:52:17 (): bar
tee -a 复制到标准输出并附加到 $LogFile
玩得开心
有两种方法,首先,我认为更好的是,创建一个 bash 文件并将结果传递给它,如下所示:
make 2>&1 > ./LogMsg
第二种方法是将结果作为参数传递给函数:
LogMsg $(make 2>&1)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.