【问题标题】:Create a pipe that writes to multiple files (tee)创建一个写入多个文件的管道 (tee)
【发布时间】:2011-01-18 08:06:37
【问题描述】:

我想在 ksh 脚本(使用 exec)中创建一个管道,该管道连接到 tee,并将输出发送到管道。

当前:

#Redirect EVERYTHING
exec 3>&1 #Save STDOUT as 3
exec 4>&2 #Save STDERR as 4
exec 1>${Log} #Redirect STDOUT to a log
exec 2>&1 #Redirect STDERR to STDOUT

喜欢做什么(但我的语法不正确):

#Redirect EVERYTHING
exec 3>&1 #Save STDOUT as 3
exec 4>&2 #Save STDERR as 4
exec 1>tee -a ${Log} >&3  #Redirect STDOUT to a log
exec 2>&1 #Redirect STDERR to STDOUT

如何创建这个管道?

【问题讨论】:

  • 至少在 bash 中,每个块中的最后一行最好描述为“将 STDOUT 附加到 STDERR”或“将 STDERR 重定向到 STDOUT”。之后 2>dump-stdout 将是空的,但是任何你 echo >&2 的东西都会和你 echo >&1 的东西去同一个地方。

标签: shell pipe ksh


【解决方案1】:

我知道 bash 不是 ksh,但有很多重叠,所以也许这也可以。

process1 N> >(process2)

创建一个子shell运行进程2。该 subshel​​l 接收来自 process1 的文件描述符 N 的数据作为其标准输入。所以特别是,您可以这样做:

process1 1> >(tee -a mylog >&3)

我不知道如果将process1 替换为exec 是否也可以,但你可以尝试一下。

【讨论】:

    【解决方案2】:

    在 ksh 中有 |&>&p,但我无法让它们执行您想要的操作。也许你可以。

    【讨论】:

      【解决方案3】:

      代替:

      exec 1>tee -a ${Log} >&3

      简单地做:

      tee -a ${Log} >&3 &

      tee 将分叉到后台,并像 tee 分叉时一样使用调用进程(即您的脚本)的 STDIN。

      【讨论】:

      • 它在我的测试中不起作用。 Tee 启动并写入日志,但没有消耗标准输出。
      • 您的文件句柄设置中可能有其他问题;启动整个过程并使用lsof 来查看/验证去哪里。
      【解决方案4】:

      我使用named pipes 制定了一个解决方案。

      #!/bin/ksh
      
      LOG=~/testLog.log
      PIPE=~/logPipe
      mkfifo ${PIPE}
      exec 3>&1 #Save STDOUT as 3
      exec 4>&2 #Save STDERR as 4
      tee -a ${LOG} <${PIPE} >&3 & #Start tee off the logpipe in the background
      exec 1>${PIPE} #Redirect stdout to the pipe
      exec 2>&1 #Redirect STDERR to STDOUT
      
      echo "TEST"
      echo Test 2
      
      ls | grep -i "test"
      
      rm -f ${PIPE} #Remove the pipe
      

      【讨论】:

      • 一个非常简洁的解决方案。谢谢。
      【解决方案5】:

      这是我使用的解决方案。它在我的 Mac 上的 ksh 下工作。它很好地封装在 start_logging() 和 stop_logging() 函数中,让生活更轻松。

      代码在实践中是这样的:

      # Optional:
      #   Set the name and location of the log file.
      #   OUTPUT_LOG=output.log    # default
      #   Set the name and location of the named pipe used.
      #   OUTPUT_PIPE=output.pipe  # default
      
      start_logging
      # Default is to append to an existing log file.
      # start_logging delete_existing_logfile
      echo "This is on standard out"
      echo "This is on standard err" >&2
      stop_logging
      

      这是整个文件。 start 和 stop 函数以及上面的示例都在文件的底部。为了使其更易于使用,只需将启动和停止函数放在它们自己的文件中,然后在需要日志记录的脚本中获取它们。

      #!/bin/sh
      
      # Author: Harvey Chapman <hchapman _AT_ 3gfp.com>
      # Description: POSIX shell functions that can be used with tee to simultaneously put
      #              stderr and stdout to both a file and stdout
      #
      # Based on:
      #    Re: How to redirect stderr and stdout to a file plus display at the same time
      #    http://www.travishartwell.net/blog/2006/08/19_2220
      
      #
      # Original example function from Travis Hartwell's blog.
      # Note: I've made minor changes to it.
      example()
      {
        OUTPUT_LOG=output.log
        OUTPUT_PIPE=output.pipe
      
        # This should really be -p to test that it's a pipe.
        if [ ! -e $OUTPUT_PIPE ]; then
            mkfifo $OUTPUT_PIPE
        fi
      
        # This should really be -f to test that it's a regular file.
        if [ -e $OUTPUT_LOG ]; then
            rm $OUTPUT_LOG
        fi
      
        exec 3>&1 4>&2
        tee $OUTPUT_LOG < $OUTPUT_PIPE >&3 &
        tpid=$!
        exec > $OUTPUT_PIPE 2>&1
      
        echo "This is on standard out"
        echo "This is on standard err" >&2
      
        exec 1>&3 3>&- 2>&4 4>&-
        wait $tpid
      
        rm $OUTPUT_PIPE
      }
      
      # A slightly reduced version of example()
      example2()
      {
        OUTPUT_LOG=output.log
        OUTPUT_PIPE=output.pipe
      
        rm -f $OUTPUT_PIPE
        mkfifo $OUTPUT_PIPE
        rm -f $OUTPUT_LOG
      
        tee $OUTPUT_LOG < $OUTPUT_PIPE &
        tpid=$!
      
        exec 3>&1 4>&2 >$OUTPUT_PIPE 2>&1
      
        echo "This is on standard out"
        echo "This is on standard err" >&2
      
        exec 1>&3 3>&- 2>&4 4>&-
        wait $tpid
        rm -f $OUTPUT_PIPE
      }
      
      #
      # Logging methods based on above. See the example below for how to use them.
      #
      
      # Usage: start_logging [delete_existing_logfile]
      start_logging()
      {
        # Check to see if OUTPUT_LOG and OUTPUT_PIPE need to be defined.
        if [ -z "$OUTPUT_LOG" ]; then
          OUTPUT_LOG=output.log
        fi
        if [ -z "$OUTPUT_PIPE" ]; then
          OUTPUT_PIPE=output.pipe
        fi
        # Make sure that we're not already logging.
        if [ -n "$OUTPUT_PID" ]; then
          echo "Logging already started!"
          return 1
        fi
      
        # Always remove the log and pipe first.
        rm -f $OUTPUT_PIPE
        # Delete the logfile first if told to.
        if [ "$1" = delete_existing_logfile ]; then
          rm -f $OUTPUT_LOG
        fi
      
        mkfifo $OUTPUT_PIPE
        tee -a $OUTPUT_LOG < $OUTPUT_PIPE &
        OUTPUT_PID=$!
      
        exec 3>&1 4>&2 >$OUTPUT_PIPE 2>&1
      }
      
      stop_logging()
      {
        # Make sure that we're currently logging.
        if [ -z "$OUTPUT_PID" ]; then
          echo "Logging not yet started!"
          return 1
        fi
        exec 1>&3 3>&- 2>&4 4>&-
        wait $OUTPUT_PID
        rm -f $OUTPUT_PIPE
        unset OUTPUT_PID
      }
      
      example3()
      {
        start_logging
        #start_logging delete_existing_logfile
        echo "This is on standard out"
        echo "This is on standard err" >&2
        stop_logging
      }
      
      #example
      #example2
      example3
      

      【讨论】:

      • 感谢提供实现,我真的很喜欢这些功能。
      猜你喜欢
      • 2014-08-31
      • 1970-01-01
      • 2021-11-23
      • 2010-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-28
      相关资源
      最近更新 更多