我正在为bash脚本添加一些自定义日志记录功能,并且无法弄清楚为什么它不会从一个命名管道获取输出并将其反馈到另一个命名管道。
以下是脚本的基本版本(http://pastebin.com/RMt1FYPc):
#!/bin/bash
PROGNAME=$(basename $(readlink -f $0))
LOG="$PROGNAME.log"
PIPE_LOG="$PROGNAME-$$-log"
PIPE_ECHO="$PROGNAME-$$-echo"
# program output to log file and optionally echo to screen (if $1 is "-e")
log () {
if [ "$1" = '-e' ]; then
shift
$@ > $PIPE_ECHO 2>&1
else
$@ > $PIPE_LOG 2>&1
fi
}
# create named pipes if not exist
if [[ ! -p $PIPE_LOG ]]; then
mkfifo -m 600 $PIPE_LOG
fi
if [[ ! -p $PIPE_ECHO ]]; then
mkfifo -m 600 $PIPE_ECHO
fi
# cat pipe data to log file
while read data; do
echo -e "$PROGNAME: $data" >> $LOG
done < $PIPE_LOG &
# cat pipe data to log file & echo output to screen
while read data; do
echo -e "$PROGNAME: $data"
log echo $data # this doesn't work
echo -e $data > $PIPE_LOG 2>&1 # and neither does this
echo -e "$PROGNAME: $data" >> $LOG # so I have to do this
done < $PIPE_ECHO &
# clean up temp files & pipes
clean_up () {
# remove named pipes
rm -f $PIPE_LOG
rm -f $PIPE_ECHO
}
#execute "clean_up" on exit
trap "clean_up" EXIT
log echo "Log File Only"
log -e echo "Echo & Log File"
我认为34号线上的命令和35将从$data
获取$PIPE_ECHO
并将其输出到$PIPE_LOG
。但是,它不起作用。相反,我必须将该输出直接发送到日志文件,而不通过$PIPE_LOG
。
为什么这不符合预期?
编辑:我将shebang改为“bash”。但问题是一样的。解决方案:A.H.'s answer帮助我理解我没有正确使用命名管道。我已经解决了我的问题,甚至没有使用命名管道。该解决方案位于:http://pastebin.com/VFLjZpC3
答案 0 :(得分:9)
可能部分是这样的:消费者将读取数据,直到没有更多数据。没有更多数据意味着,在read
调用时没有生产者打开命名管道。这意味着只有在没有至少一个生产者的时间点没有时间点的情况下,多个生产者才能喂养一个消费者。想一想自动关闭的门:如果有一个稳定的人流,通过将门把手递给下一个门或者同时挤压多个人来保持门始终打开,门就打开了。但是一旦门关闭,它就会一直关闭。
一点点演示应该会使区别变得更加清晰:
打开三个炮弹。 第一次 shell:
1> mkfifo xxx
1> cat xxx
没有显示输出,因为cat
已打开命名管道并正在等待数据。
第二次 shell:
2> cat > xxx
没有输出,因为这个cat
是一个生产者,它使命名管道保持打开状态,直到我们告诉他明确地关闭它。
第三次 shell:
3> echo Hello > xxx
3>
该生产者立即返回。
第一个shell :
Hello
消费者收到数据,写下来并且 - 由于还有一个消费者保持开门,继续等待。
第三个外壳
3> echo World > xxx
3>
第一个shell :
World
消费者收到数据,写下来并且 - 由于还有一个消费者保持开门,继续等待。
第二个Shell :写入cat > xxx
窗口:
And good bye!
(control-d key)
2>
第一个shell
And good bye!
1>
^ D 键关闭了最后一个生产者cat > xxx
,因此消费者也退出了。
在你的情况下,这意味着:
log
函数会尝试多次打开和关闭管道。不是个好主意。while
循环都比您想象的更早退出。 (使用(while ... done < $PIPE_X; echo FINISHED; ) &
sleep 1
的末尾添加log
{1}}功能。)sleep
),因为您的生产者可能找不到任何消费者所以我可以解释代码中的问题,但我不能告诉你一个解决方案,因为不清楚你的需求的边缘是什么。
答案 1 :(得分:0)
似乎问题出现在“cat pipe data to log file”部分。
让我们看看:你使用“&amp;”把循环放在后台,我想你的意思是它必须与第二个循环并行运行。
但问题是你甚至不需要“&amp;”,因为只要fifo中没有更多数据可用,while..read就会停止。 (至少你必须先得到一些第一次阅读才能工作)。如果没有更多数据可用,则下一次读取不会挂起(这会带来另一个问题:程序如何停止?)。
我想while读取会在执行读取之前检查文件中是否有更多数据可用,如果不是这样,则会停止。
您可以查看此示例:
mkfifo foo
while read data; do echo $data; done < foo
此脚本将挂起,直到您从另一个shell(或第一个shell)写入任何内容。但只要阅读有效,它就会结束。
编辑: 我已经在RHEL 6.2上进行了测试,它就像你说的那样工作(例如:糟糕!)。
问题在于,在运行脚本(假设脚本为“a”)之后,你还有一个“a”进程。所以,是的,在某种程度上,脚本像我之前写的那样挂起(不是那个愚蠢的答案,因为我认为:))。除非您只写一个日志(仅限日志文件或回显,在这种情况下它可以工作)。
(这是PIPE_ECHO的读取循环,在写入PIPE_LOG时会挂起并且每次都会运行一个进程)。
我添加了一些调试消息,这就是我所看到的:
当你ls -l / proc / [pid] / fd时,你可以看到fifo仍然是打开的(但已被删除)。 事实上,脚本退出并删除了fifos,但仍然有一个进程使用它。 如果你没有在清理时删除log fifo并将其捕获,它将释放挂起过程。
希望它会有所帮助...