创建写入多个文件的管道 (tee)

发布于 2024-08-22 00:48:45 字数 580 浏览 8 评论 0原文

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

当前:

#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

如何创建此管道?

I would like to create a pipe in a ksh script (using exec) that pipe's to a tee, and sends the output to a pipe.

Current:

#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

What'd I'd like to do (but I don't have the syntax correct):

#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

How can I create this pipe?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

转角预定愛 2024-08-29 00:48:45

我使用命名管道制定了一个解决方案。

#!/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

I worked out a solution using 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
陪我终i 2024-08-29 00:48:45

这是我使用的解决方案。它在我的 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

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

#!/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

Here's a solution I use. It works under ksh on my Mac. It's nicely encapsulated into start_logging() and stop_logging() functions to make life easy.

The code looks like this in practice:

# 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

Here is the whole file. The start and stop functions along with the example above are all at the bottom of the file. To make it easier to use, just put the start and stop functions in their own file and source them in the scripts where you need the logging.

#!/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
酒废 2024-08-29 00:48:45

我知道 bash 不是 ksh,但是有很多重叠,所以也许这也适用于那里。

process1 N> >(process2)

创建一个运行 process2 的子 shell。该子 shell 将来自 process1 的文件描述符 N 的数据作为其标准输入接收。因此,特别是,您可以这样做:

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

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

I know bash not ksh, but there's a lot of overlap, so maybe this will work there too.

process1 N> >(process2)

Creates a subshell running process2. That subshell receives as its stdin the data from process1's file descriptor N. So in particular, you could do:

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

I don't know whether this would also work if process1 is replaced with exec, but you could give it a try.

闻呓 2024-08-29 00:48:45

ksh 中有 |&>&p,但我无法让它们做你正在寻找的事情。也许你可以。

There are |& and >&p in ksh, but I couldn't get them to do what you're looking for. Maybe you can.

清欢 2024-08-29 00:48:45

而不是:

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

简单地执行:

tee -a ${Log} >&3 &

< code>tee 将分叉到后台,并将消耗调用进程的“(即您的脚本的)STDIN,就像 tee 分叉时一样。

Instead of:

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

do simply:

tee -a ${Log} >&3 &

tee will fork into the background, and will consume the calling process' (i.e. your script's) STDIN as it was at the time that tee forked.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文