4

別のプロセスに転送するために、bash スクリプトで複数の子プロセスの出力を 1 行ずつ収集しようとしています。

サブプロセスの出力が混在していないことを保証するものは何も見つかりませんでしたが、各出力行が正しく出力に送られることが重要です。出力間の順序は重要ではありません。

混合/文字化けした出力の例を次に示します。

#!/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 並列で同じことを実行することは仕事の一部ですが、私の環境ではオプションではありません。

GNU 並列は、コマンドからの出力が、コマンドを順番に実行した場合と同じ出力であることを確認します。これにより、GNU パラレルからの出力を他のプログラムの入力として使用することが可能になります。

そのため、GNU parallel は、各ジョブが終了するとすぐに各ジョブ出力を書き込み、出力を混合しないようにします。それは良い。しかし、各ジョブの出力をできるだけ早く取得することにも関心があります。つまり、ジョブの終了を待たないことです。「-u」スイッチがありますが、ジョブの出力が混在します。

fifo で遊んだり、選択したり、perl スクリプトを作成したりする必要がありますか?

--

出力がman 7パイプで混合される理由/方法/時期を見つけたと思います

POSIX.1-2001 は、PIPE_BUF バイト未満の write(2) はアトミックでなければならないと述べています。出力データは連続したシーケンスとしてパイプに書き込まれます。PIPE_BUF バイトを超える書き込みはアトミックではない可能性があります。カーネルは、データを他のプロセスによって書き込まれたデータとインターリーブする可能性があります。POSIX.1-2001 では、PIPE_BUF が少なくとも 512 バイトである必要があります。(Linux では、PIPE_BUF は 4096 バイトです。)

4

1 に答える 1

1

これが私の最初のドローです。これは、標準入力で指定されたすべてのコマンドをバックグラウンドで開始し (それが必要かどうかはわかりません)、これらからの出力を 1 行ずつ収集する単純なスクリプトです。

#!/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>);
于 2013-05-22T18:39:15.813 に答える