我正在尝试逐行收集bash脚本中几个子进程的输出,以便将其转发到另一个进程。
我发现没有什么可以保证子过程的输出不会混合,但对我来说每条输出线都适当地输出到输出是很重要的。输出之间的顺序并不重要。
以下是混合/乱码输出的示例:
#!/bin/bash
for i in {1..1000}; do
( { echo BEGIN; dmesg; echo END; } | tr -d '\n'; echo ) &
done
wait
运行此:
$ ./test_output.sh | perl -ne 'print "$1\n" if m/(.{1,20}BEGIN.{0,20})/' | head
0.000000] SRAT: PXMBEGIN[ 0.000000] Initi
ME through PCIe PME BEGIN[ 0.000000] Initi
ME through PCIe PME BEGIN[ 0.000000] Initi
[ 0.209816] pci 0BEGIN[ 0.000000] Initi
ciehp 0000:00:16.1:pBEGIN[ 0.000000] Initi
CI: Updating contextBEGIN[ 0.000000] Initi
l family 2[ 0.588BEGIN[ 0.000000] Initi
ME through PCIe PME BEGIN[ 0.000000] Initi
CI: Updating contextBEGIN[ 0.000000] Initi
3922 pages, LIFO batBEGIN[ 0.000000] Initi
您可以看到包含混合内容的多行。
当然,没有&
一切都很好。
所以现在,我别无选择,只能将每个孩子的输出重定向到一个文件,然后在一个大的wait
,cat
所有这些文件之后。
使用GNU parallel运行相同功能可以完成部分工作,但在我的环境中它不是一个选项。
GNU parallel确保命令的输出与输出相同 如果顺序运行命令,你会得到的。这样做 可以使用GNU parallel的输出作为其他程序的输入。
因此,GNU并行将在每个作业完成后立即写入每个作业输出,并且它负责不混合输出。非常好。但我也有兴趣尽快获得每份工作的输出,即不等待工作退出。有“-u”开关,但它会混合作业输出。
我是否需要使用fifo,选择甚至编写perl脚本?
-
我想我已经找到了为什么/如何/何时将输出混合在man 7 pipe
中POSIX.1-2001表示写入(2)s必须小于PIPE_BUF字节 atomic:输出数据作为连续内容写入管道 序列。超过PIPE_BUF字节的写入可能是非原子的: 内核可以将数据与其他进程写入的数据交错。 POSIX.1-2001要求PIPE_BUF至少为512字节。 (在Linux上, PIPE_BUF是4096字节。)
答案 0 :(得分:1)
这是我的第一次抽奖。这是一个简单的脚本,可以在后台启动stdin上给出的所有命令(不确定这是我想要的)并逐行收集这些命令。
#!/usr/bin/env perl
use strict;
use warnings;
use IO::Select;
use POSIX qw(strftime);
my $SELECT_TIMEOUT = 1;
my $TAG_SEPARATOR = '|';
my $TAG_TIMESTAMP_FORMAT = '%Y-%m-%dT%H:%M:%S';
sub multiplex {
my @commands = @_;
my %tags = (); # fd -> cmd
my $sel = IO::Select->new();
for my $cmd (@commands) {
$cmd =~ s/^\s+|\s+$//g;
my $fd;
if (!open($fd, "-|", $cmd)) {
warn "Cannot start '$cmd': $!";
next;
}
else {
$tags{$fd} = $cmd;
$sel->add($fd);
}
}
while ($sel->handles > 0) {
my @handles = $sel->can_read($SELECT_TIMEOUT);
# maybe something went wrong
if (!@handles) {
for my $fd ($sel->has_exception($SELECT_TIMEOUT)) {
$sel->remove($fd);
}
next;
}
my $now = strftime($TAG_TIMESTAMP_FORMAT, localtime(time()));
for my $fd (@handles) {
if (defined(my $line = <$fd>)) {
if ($TAG_SEPARATOR) {
$line = join($TAG_SEPARATOR, $now, $tags{$fd}, $line);
}
print $line;
}
else {
# EOF
$sel->remove($fd);
}
}
}
}
multiplex(<STDIN>);