Skip to main content
print an error message if cmd cannot be exec'ed / add some notes
Source Link
user313992
user313992

notes:

  1. code like delete $$_[1] for @cl will not only remove the file handles from the array, but will also close them immediately, because there's no other reference pointing to them; this is different from (properly) garbage collected languages like javascript.

  2. the exit status of ytee will reflect the exit statuses of the command and filters; this could be changed/simplified.

notes:

  1. code like delete $$_[1] for @cl will not only remove the file handles from the array, but will also close them immediately, because there's no other reference pointing to them; this is different from (properly) garbage collected languages like javascript.

  2. the exit status of ytee will reflect the exit statuses of the command and filters; this could be changed/simplified.

print an error message if cmd cannot be exec'ed
Source Link
user313992
user313992
#! /usr/bin/perl # usage: ytee [-r irs] { command | - } [filter ..] use strict; if($ARGV[0] =~ /^-r(.+)?/){ shift; $/ = eval($1 // shift); die $@ if $@ } elsif(! -t STDIN){ $/ = \0x8000 } 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: $!"; } 
#! /usr/bin/perl # usage: ytee [-r irs] { command | - } [filter ..] use strict; if($ARGV[0] =~ /^-r(.+)?/){ shift; $/ = eval($1 // shift); die $@ if $@ } elsif(! -t STDIN){ $/ = \0x8000 } 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; } 
#! /usr/bin/perl # usage: ytee [-r irs] { command | - } [filter ..] use strict; if($ARGV[0] =~ /^-r(.+)?/){ shift; $/ = eval($1 // shift); die $@ if $@ } elsif(! -t STDIN){ $/ = \0x8000 } 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: $!"; } 
link to similar questions
Source Link
user313992
user313992

with its standard input piped to filter1, filter2, filter3, ... in parallel, as if it were with tee >(filter1) >(filter2) >(filter3) ....

tee >(filter1) >(filter2) >(filter3) ... 

This is also an answer for the two very similar questions: here and here.

with its standard input piped to filter1, filter2, filter3, ... in parallel, as if it were with tee >(filter1) >(filter2) >(filter3) ....

with its standard input piped to filter1, filter2, filter3, ... in parallel, as if it were with

tee >(filter1) >(filter2) >(filter3) ... 

This is also an answer for the two very similar questions: here and here.

some extra rationale
Source Link
user313992
user313992
Loading
simplify; 'open3' dies on failure, no need to check for its return value
Source Link
user313992
user313992
Loading
Source Link
user313992
user313992
Loading