Создайте канал, который пишет в несколько файлов (тройник) - PullRequest
6 голосов
/ 18 февраля 2010

Я хотел бы создать канал в сценарии 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

Как я могу создать эту трубу?

Ответы [ 5 ]

6 голосов
/ 05 марта 2010

Я разработал решение, используя именованные каналы .

#!/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 голосов
/ 05 марта 2010

Вот решение, которое я использую. Он работает под Ksh на моем Mac. Он красиво инкапсулирован в функции 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
0 голосов
/ 19 февраля 2010

В ksh есть |& и >&p, но я не мог заставить их делать то, что вы ищете. Может быть, вы можете.

0 голосов
/ 27 февраля 2010

Вместо:

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

сделать просто:

tee -a ${Log} >&3 &

tee перейдет в фоновый режим и будет использовать вызывающий процесс '(т. Е. Ваш скрипт) STDIN, как это было в то время, когда tee разветвлялся.

0 голосов
/ 19 февраля 2010

Я знаю, что bash не ksh, но есть много совпадений, так что, возможно, это тоже будет работать.

process1 N> >(process2)

Создает подоболочку, выполняющую процесс2.Этот подоболочек получает в качестве своего стандартного источника данные из файлового дескриптора процесса N. Так, в частности, вы можете сделать:

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

Я не знаю, будет ли это также работать, если process1 заменить на exec, но вы можете попробовать.

...