Como a resposta aceita é usar perl
, você também pode fazer a coisa toda em perl
, sem outras ferramentas não-padrão e recursos de shell não padrão, e sem carregar blocos de dados imprevisivelmente longos no memória, ou outros tais misfeatures horríveis.
O script ytee
do final desta resposta, quando usado desta maneira:
ytee command filter1 filter2 filter3 ...
funcionará como
command <(filter1) <(filter2) <(filter3) ...
com sua entrada padrão canalizada para filter1
, filter2
, filter3
, ... em paralelo, como se estivesse com
tee >(filter1) >(filter2) >(filter3) ...
Exemplo:
echo 'Line 1
Line B
Line iii' | ytee 'paste' 'sed s/B/b/g | nl' 'sed s/iii/III/ | nl'
1 Line 1 1 Line 1
2 Line b 2 Line B
3 Line iii 3 Line III
Esta também é uma resposta para as duas perguntas muito semelhantes: aqui e aqui .
ytee :
#! /usr/bin/perl
# usage: ytee [-r irs] { command | - } [filter ..]
use strict;
if($ARGV[0] =~ /^-r(.+)?/){ shift; $/ = eval($1 // shift); die $@ if $@ }
elsif(! -t STDIN){ $/ = ytee command filter1 filter2 filter3 ...
x8000 }
my $cmd = shift;
my @cl;
for(@ARGV){
use IPC::Open2;
my $pid = open2 my $from, my $to, $_;
push @cl, [$from, $to, $pid];
}
defined(my $pid = fork) or die "fork: $!";
if($pid){
delete $$_[0] for @cl;
$SIG{PIPE} = 'IGNORE';
my ($s, $n);
while(<STDIN>){
for my $c (@cl){
next unless exists $$c[1];
syswrite($$c[1], $_) ? $n++ : delete $$c[1]
}
last unless $n;
}
delete $$_[1] for @cl;
while((my $p = wait) > 0){ $s += !!$? << ($p != $pid) }
exit $s;
}
delete $$_[1] for @cl;
if($cmd eq '-'){
my $n; do {
$n = 0; for my $c (@cl){
next unless exists $$c[0];
if(my $d = readline $$c[0]){ print $d; $n++ }
else{ delete $$c[0] }
}
} while $n;
}else{
exec join ' ', $cmd, map {
use Fcntl;
fcntl $$_[0], F_SETFD, fcntl($$_[0], F_GETFD, 0) & ~FD_CLOEXEC;
'/dev/fd/'.fileno $$_[0]
} @cl;
die "exec $cmd: $!";
}
notas:
-
o código como
delete $$_[1] for @cl
removerá não apenas as alças de arquivo da matriz, mas tambémas fechará imediatamente , porque não há outra referência apontando para elas; isso é diferente de (corretamente) idiomas coletados como lixo javascript
. -
o status de saída de
ytee
refletirá os status de saída dos filtros de comando e ; isso pode ser alterado / simplificado.