Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl: Pass one byte plus STDIN to another command

Tags:

io

perl

I would like to do this efficiently:

my $buf;
my $len = read(STDIN,$buf,1);
if($len) {
    # Not empty                                                                                                          
    open(OUT,"|-", "wc") || die;
    print OUT $buf;
    # This is the line I want to do faster
    print OUT <STDIN>;
    exit;
}

The task is to start wc only if there is any input. If there is no input, the program should just exit.

wc is just an example here. It will be substituted with a much more complex command.

The input can be of several TB of data, so I would really like to not touch that data at all (not even with a sysread). I tried doing:

    pipe(STDIN,OUT);

But that doesn't work. Is there some other way that I can tell OUT that after it has gotten the first byte, it should just read from STDIN? Maybe some open(">=&2") gymnastics combined with exec?

like image 655
Ole Tange Avatar asked Apr 15 '15 22:04

Ole Tange


1 Answers

The FIONREAD ioctl, mentioned in the Perl Cookbook, can tell you how many bytes are pending on a file descriptor without consuming them. In perlish terms:

use strict;
use warnings;

use IO::Select qw( );    
BEGIN { require 'sys/ioctl.ph'; }

sub fionread {
    my $sz = pack('L', 0);
    return unless ioctl($_[0], FIONREAD, $sz);
    return unpack('L', $sz);
}

# Wait until it's known whether the handle has data to read or has reached EOF.
IO::Select->new(\*STDIN)->can_read();

if (fionread(\*STDIN)) {
    system('wc');
    # Check for errors
}

This should be very widely portable to UNIX and UNIX-like platforms.

like image 97
pilcrow Avatar answered Nov 16 '22 02:11

pilcrow