使用 mkpipe 进行 IO 重定向以进行日志记录

使用 mkpipe 进行 IO 重定向以进行日志记录

我有一堆将输出发送到 stdout 的脚本。我将输出重定向到文件,但这些文件很快就会变得很大。例如:

./script_with_lots_of_outpu.sh 2>&1 mylog.txt &

我想将输出发送到命名管道,以便类似以下脚本的程序可以切换正在写入的文件:

#!/bin/bash
if [ $# -ne 2 ]; then
echo "USAGE: ./redir.sh pipename filename" 
fi

pipename=$1
filename=$2
trap filename="`date +%s`$filename" 2
mkfifo $pipename

while [ 1 -eq 1 ]
do
    read input
    echo $input >> $filename
done < $pipename

可以向该脚本发送 CTRL-C(或其他信号),它将有效地导致管道的输出开始写入不同的文件(前面加上时间戳)。

当我运行此脚本并向其回显某些内容时,它开始写入大量空行:

   > ./redir.sh testpipe 测试文件 &
   > echo "这是一个测试" > testpipe
   > wc-l 测试文件
   627915 测试文件

如何让 redir.sh 仅在其读取的管道被写入文件时才写入文件?

编辑

最终产品似乎运行如下。我需要进行更多测试以确定它是否值得投入生产

#!/bin/bash

if [ $# -ne 2 ]; then
    echo "USAGE: ./redir.sh pipename filename" 
    exit -1
fi

pipename=$1; rm $pipename;
origname=$2.log
filename=$2

rename()
{
    filename="$origname-`date +%s`"
    mv $origname $filename
    nohup nice -n 20 tar -czvf $filename.tar.gz $filename &
    trap rename 2
}

mkfifo $pipename
trap rename 2

while [ 1 -eq 1 ]
do
    read input
    echo $input >> $origname
done <> $pipename

答案1

关于从命名管道读取数据,有一个注意事项。当您打开命名管道时,您的open()调用将挂起,直到写入者出现。当写入者出现时,后续read()调用将返回写入者写入管道的任何数据。但是,当写入者关闭管道(或退出)时,调用read()将开始返回 0(而不是阻塞)。

这就是为什么

int fd = open("testpipe", O_RDONLY);

你可能想打开这样的管道

int fd = open("testpipe", O_RDWR);

这样,进程就不仅是读取者,也是写入者。即使你实际上从未向管道写入任何内容,这也确保了写入者存在因此read()调用不会返回 0,而是阻塞并等待某个写入器将某些内容写入管道。

现在,当你的脚本执行以下操作时:

while [ 1 -eq 1 ]
do
    read input
    ...
done < $pipename

你的 shell 打开管道仅供读取(O_RDONLY)。

解决您的问题的方法是让 shell 打开管道以进行读写,如下所示:

while [ 1 -eq 1 ]
do
    read input
    ...
done <> $pipename

注意将 替换为< $pipename<> $pipename这使得 shell 打开管道进行读取和写入 ( O_RDWR)。

相关内容