后台工作的乱码输出

时间:2013-05-12 16:10:45

标签: bash parallel-processing

我正在尝试逐行收集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

您可以看到包含混合内容的多行。

当然,没有&一切都很好。

所以现在,我别无选择,只能将每个孩子的输出重定向到一个文件,然后在一个大的waitcat所有这些文件之后。

使用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字节。)

1 个答案:

答案 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>);