bash 将 stdout 和 stderr 重定向到 Function

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11904907/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 02:58:43  来源:igfitidea点击:

Redirect stdout and stderr to Function

bash

提问by Ryan

I need help sending the output (stdin and stdout) from system commands to a bash function, while still accepting input from arguments. Something like the example that follows. Can someone point me down the right road?

我需要帮助将系统命令的输出(stdin 和 stdout)发送到 bash 函数,同时仍然接受来自参数的输入。类似于下面的例子。有人可以指出我正确的道路吗?

LogMsg()
{
  DateTime=`date "+%Y/%m/%d %H:%M:%S"`
  echo '*****'$DateTime' ('$QMAKESPEC'): ' >> "$LogFile"
  echo $DateTime' ('$QMAKESPEC'): '
}

# 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)

采纳答案by Lee Netherton

To do this you can use the readbash builtin:

为此,您可以使用readbash 内置命令:

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
}

And then use a pipe:

然后使用管道:

make 2>&1 | LogMsg

Update:

更新:

To be able to use stdin OR an argument as input (as per chepner's comment) you can do this:

为了能够使用 stdin 或参数作为输入(根据 chepner 的评论),您可以执行以下操作:

LogMsg()
{
  if [ -n "" ]
  then
      IN=""
  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
}

回答by William_K

It's an old thread.. but I have used it to help me write a log function that will output also multiple lines of a command output:

这是一个旧线程..但我用它来帮助我编写一个日志函数,该函数还将输出多行命令输出:

# Defines function to grab a time stamp #
get_Time () { Time=$(date +%Y-%m-%d\ %H:%M:%S) ; }

write_Log()
{
get_Time
if [ -n "" ]; then         # If it's from a "<message>" then set it
    IN=""
    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
}

回答by Maverick

Thanks to people who posted their responses. I came up with my version which will add timestamp once per message.

感谢发布回复的人。我想出了我的版本,它将为每条消息添加一次时间戳。

#!/bin/bash
CURRENT_PID=$$
PROCESS_NAME=$(basename 
scriptFolder=$(cd $(dirname "
# 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 "" ]; 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 "" ]; 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=
  # Set the global logfile, in case it was not saved
  if [ -n "" ]; then
    logFile=
  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 "" ]; 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 "" ]; 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
}
") && 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
) LOGFILE=/var/log/backup-monitor.log function log_message { if [ -n "" ]; then MESSAGE="" 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

回答by smalers

Based on the previous answers, I put together some generic functions that work with or without a log file, as listed at the end of this post. These are handy for more complex scripts. I generally print terminal window messages to stderrso as to not interfere with legitimate program output that may need to be redirected. The functions can be called as follows:

根据之前的答案,我整理了一些可以使用或不使用日志文件的通用函数,如本文末尾所列。这些对于更复杂的脚本很方便。我通常会打印终端窗口消息,stderr以免干扰可能需要重定向的合法程序输出。这些函数可以被调用如下:

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

Functions...

职能...

make 2>&1 > ./LogMsg

回答by 0800peter

In my opinion, a timeout of 100ms ( -t 0.1 ) in read command will allow the LogMsg to handle input piping and parameters without waiting forever in case of no input.

在我看来,读取命令中 100 毫秒(-t 0.1)的超时将允许 LogMsg 处理输入管道和参数,而无需在没有输入的情况下永远等待。

LogMsg $(make 2>&1)

tee -a duplicates to stdout and appends to $LogFile

tee -a 复制到标准输出并附加到 $LogFile

have fun

玩得开心

回答by PLuS

There are 2 ways of doing so, first, which I think is better, is to create a bash file and pass the result to it like this:

有两种方法可以这样做,首先,我认为更好的是创建一个 bash 文件并将结果传递给它,如下所示:

##代码##

the second way is to pass result as an argument to function:

第二种方法是将结果作为参数传递给函数:

##代码##