2013-05-12 49 views
4

我想逐行收集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並行相同的任務是部分工作,但它不是我的環境中的一個選項。

GNU並行可以確保命令的輸出與 的輸出相同,只要您按順序運行命令即可。這使得 可能使用來自GNU並行的輸出作爲其他程序的輸入。

因此,GNU並行將在每個作業完成後立即寫入每個作業輸出,並且它會處理不混合輸出。那很好。但我也希望儘快獲得每項工作的輸出,即不要等待工作退出。有「-u」開關,但它會混合作業輸出。

我需要玩fifo,選擇,甚至寫一個perl腳本嗎?

-

我想我已經找到爲什麼/如何/時的輸出在男子7管

POSIX.1-2001弄混說,寫(2)的少於一PIPE_BUF字節必須爲 原子:輸出數據作爲連續的 序列寫入管道。超過PIPE_BUF字節的寫入可能是非原子的: 內核可能會將數據與其他進程寫入的數據交錯。 POSIX.1-2001要求PIPE_BUF至少爲512字節。 (在Linux上, PIPE_BUF是4096字節。)

+0

是否重要的​​是你儘快得到任何輸出,或者如果你一旦完成所有事情都可以得到它,那很重要嗎? – Teddy 2013-05-12 16:27:39

+0

我想盡快得到它,是的。否則,無論您將其存儲在內存還是每個進程的文件中,我都認爲這是相同的解決方案。 – 2013-05-12 16:49:51

+1

您能詳細說明爲什麼10秒安裝的GNU並行(wget -O - pi.dk/3 | sh)不起作用?是否覆蓋:http://oletange.blogspot.dk/2013/04/why-not-install-gnu-parallel.html – 2013-05-12 20:37:20

回答

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 ([email protected]) { 
     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>);