Created
November 7, 2019 17:57
-
-
Save webstrand/eb088654a2a3f2025515e44b9f3dfe49 to your computer and use it in GitHub Desktop.
A `cat`-like program which exits when STDOUT is closed. Useful for piping files followed by stdin: `sipe some_file /dev/stdin | your-program`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl | |
use strict; | |
use warnings; | |
use v5.26; | |
use Errno qw(EAGAIN EWOULDBLOCK EINTR); | |
use Fcntl qw(O_WRONLY); | |
STDOUT->autoflush(1); | |
# This is a tool like cat, except that it watches for STDOUT close and exits | |
# immediately. | |
if(do { no warnings; defined sysread(STDOUT, my $tmp, 0) }) { | |
die "STDOUT is readable, please use cat"; | |
} | |
my $skipped_file; | |
OUTER: for(@ARGV) { | |
my $FH; | |
unless(open($FH, "<", $_)) { | |
warn "$_: $!"; | |
$skipped_file = 1; | |
next; | |
} | |
my $rin = ''; | |
vec($rin, fileno($FH), 1) = 1; | |
vec($rin, fileno(STDOUT), 1) = 1; | |
for(;;) { | |
my $rout = ''; | |
unless(select($rout=$rin, undef, undef, undef)) { | |
redo if not $! || $! == EINTR; | |
die "select: $!"; | |
} | |
# STDOUT becomes readable when all readers have closed the pipe. | |
last OUTER if(vec($rout, fileno(STDOUT), 1)); | |
# $FH becomes readable when there's data to be read. | |
my $buffer; | |
if(vec($rout, fileno($FH), 1)) { | |
unless(sysread($FH, $buffer, 8192)) { | |
last unless $!; | |
redo if $! == EAGAIN || $! == EWOULDBLOCK || $! == EINTR; | |
die "read: $!"; | |
} | |
} | |
print STDOUT $buffer or die "write: $!"; | |
} | |
} | |
exit($skipped_file ? 1 : 0); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment