parcat: Initial version.

This commit is contained in:
Ole Tange 2016-09-04 10:42:04 +02:00
parent 063b64e0c9
commit 196afa553b
5 changed files with 405 additions and 33 deletions

View file

@ -1,4 +1,4 @@
bin_SCRIPTS = parallel sql niceload \ bin_SCRIPTS = parallel sql niceload parcat \
env_parallel env_parallel.bash env_parallel.zsh env_parallel.fish \ env_parallel env_parallel.bash env_parallel.zsh env_parallel.fish \
env_parallel.ksh env_parallel.pdksh env_parallel.csh env_parallel.tcsh env_parallel.ksh env_parallel.pdksh env_parallel.csh env_parallel.tcsh
@ -8,13 +8,13 @@ install-exec-hook:
if DOCUMENTATION if DOCUMENTATION
man_MANS = parallel.1 env_parallel.1 sem.1 sql.1 niceload.1 \ man_MANS = parallel.1 env_parallel.1 sem.1 sql.1 niceload.1 \
parallel_tutorial.7 parallel_design.7 parallel_tutorial.7 parallel_design.7 parcat.1
doc_DATA = parallel.html env_parallel.html sem.html sql.html niceload.html \ doc_DATA = parallel.html env_parallel.html sem.html sql.html niceload.html \
parallel_tutorial.html parallel_design.html \ parallel_tutorial.html parallel_design.html parcat.html \
parallel.texi env_parallel.texi sem.texi sql.texi niceload.texi \ parallel.texi env_parallel.texi sem.texi sql.texi niceload.texi \
parallel_tutorial.texi parallel_design.texi \ parallel_tutorial.texi parallel_design.texi parcat.texi \
parallel.pdf env_parallel.pdf sem.pdf sql.pdf niceload.pdf \ parallel.pdf env_parallel.pdf sem.pdf sql.pdf niceload.pdf \
parallel_tutorial.pdf parallel_design.pdf parallel_tutorial.pdf parallel_design.pdf parcat.pdf
endif endif
# Build documentation file if the tool to build exists. # Build documentation file if the tool to build exists.
@ -61,6 +61,12 @@ niceload.1: niceload.pod
&& mv $(srcdir)/niceload.1n $(srcdir)/niceload.1 \ && mv $(srcdir)/niceload.1n $(srcdir)/niceload.1 \
|| echo "Warning: pod2man not found. Using old niceload.1" || echo "Warning: pod2man not found. Using old niceload.1"
parcat.1: parcat
pod2man --release='$(PACKAGE_VERSION)' --center='$(PACKAGE_NAME)' \
--section=1 $(srcdir)/parcat > $(srcdir)/parcat.1n \
&& mv $(srcdir)/parcat.1n $(srcdir)/parcat.1 \
|| echo "Warning: pod2man not found. Using old parcat.1"
parallel.html: parallel.pod parallel.html: parallel.pod
pod2html --title "GNU Parallel" $(srcdir)/parallel.pod > $(srcdir)/parallel.htmln \ pod2html --title "GNU Parallel" $(srcdir)/parallel.pod > $(srcdir)/parallel.htmln \
&& mv $(srcdir)/parallel.htmln $(srcdir)/parallel.html \ && mv $(srcdir)/parallel.htmln $(srcdir)/parallel.html \
@ -109,6 +115,13 @@ niceload.html: niceload.pod sql.html
|| echo "Warning: pod2html not found. Using old niceload.html" || echo "Warning: pod2html not found. Using old niceload.html"
rm -f $(srcdir)/pod2htm* rm -f $(srcdir)/pod2htm*
# Depending on niceload.html to avoid stupid pod2html race condition
parcat.html: parcat niceload.html
pod2html --title "GNU parcat" $(srcdir)/parcat > $(srcdir)/parcat.htmln \
&& mv $(srcdir)/parcat.htmln $(srcdir)/parcat.html \
|| echo "Warning: pod2html not found. Using old parcat.html"
rm -f $(srcdir)/pod2htm*
parallel.texi: parallel.pod parallel.texi: parallel.pod
pod2texi --output=$(srcdir)/parallel.texi $(srcdir)/parallel.pod \ pod2texi --output=$(srcdir)/parallel.texi $(srcdir)/parallel.pod \
|| echo "Warning: pod2texi not found. Using old parallel.texi" || echo "Warning: pod2texi not found. Using old parallel.texi"
@ -137,6 +150,10 @@ niceload.texi: niceload.pod
pod2texi --output=$(srcdir)/niceload.texi $(srcdir)/niceload.pod \ pod2texi --output=$(srcdir)/niceload.texi $(srcdir)/niceload.pod \
|| echo "Warning: pod2texi not found. Using old niceload.texi" || echo "Warning: pod2texi not found. Using old niceload.texi"
parcat.texi: parcat
pod2texi --output=$(srcdir)/parcat.texi $(srcdir)/parcat \
|| echo "Warning: pod2texi not found. Using old parcat.texi"
parallel.pdf: parallel.pod parallel.pdf: parallel.pod
pod2pdf --output-file $(srcdir)/parallel.pdf $(srcdir)/parallel.pod --title "GNU Parallel" \ pod2pdf --output-file $(srcdir)/parallel.pdf $(srcdir)/parallel.pod --title "GNU Parallel" \
|| echo "Warning: pod2pdf not found. Using old parallel.pdf" || echo "Warning: pod2pdf not found. Using old parallel.pdf"
@ -165,19 +182,23 @@ niceload.pdf: niceload.pod
pod2pdf --output-file $(srcdir)/niceload.pdf $(srcdir)/niceload.pod --title "GNU niceload" \ pod2pdf --output-file $(srcdir)/niceload.pdf $(srcdir)/niceload.pod --title "GNU niceload" \
|| echo "Warning: pod2pdf not found. Using old niceload.pdf" || echo "Warning: pod2pdf not found. Using old niceload.pdf"
parcat.pdf: parcat
pod2pdf --output-file $(srcdir)/parcat.pdf $(srcdir)/parcat --title "GNU parcat" \
|| echo "Warning: pod2pdf not found. Using old parcat.pdf"
sem: parallel sem: parallel
ln -fs parallel sem ln -fs parallel sem
DISTCLEANFILES = parallel.1 env_parallel.1 sem.1 sql.1 niceload.1 \ DISTCLEANFILES = parallel.1 env_parallel.1 sem.1 sql.1 niceload.1 \
parallel_tutorial.7 parallel_design.7 \ parallel_tutorial.7 parallel_design.7 parcat.1 \
parallel.html env_parallel.html sem.html sql.html niceload.html \ parallel.html env_parallel.html sem.html sql.html niceload.html \
parallel_tutorial.html parallel_design.html \ parallel_tutorial.html parallel_design.html parcat.html \
parallel.texi env_parallel.texi sem.texi sql.texi niceload.texi \ parallel.texi env_parallel.texi sem.texi sql.texi niceload.texi \
parallel_tutorial.texi parallel_design.texi \ parallel_tutorial.texi parallel_design.texi parcat.texi \
parallel.pdf env_parallel.pdf sem.pdf sql.pdf niceload.pdf \ parallel.pdf env_parallel.pdf sem.pdf sql.pdf niceload.pdf \
parallel_tutorial.pdf parallel_design.pdf parallel_tutorial.pdf parallel_design.pdf parcat.pdf
EXTRA_DIST = parallel sem sql niceload env_parallel \ EXTRA_DIST = parallel sem sql niceload parcat env_parallel \
env_parallel.bash env_parallel.zsh env_parallel.fish env_parallel.ksh \ env_parallel.bash env_parallel.zsh env_parallel.fish env_parallel.ksh \
env_parallel.pdksh env_parallel.csh env_parallel.tcsh \ env_parallel.pdksh env_parallel.csh env_parallel.tcsh \
sem.pod parallel.pod env_parallel.pod niceload.pod parallel_tutorial.pod \ sem.pod parallel.pod env_parallel.pod niceload.pod parallel_tutorial.pod \

View file

@ -229,30 +229,30 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@ top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@ top_builddir = @top_builddir@
top_srcdir = @top_srcdir@ top_srcdir = @top_srcdir@
bin_SCRIPTS = parallel sql niceload \ bin_SCRIPTS = parallel sql niceload parcat \
env_parallel env_parallel.bash env_parallel.zsh env_parallel.fish \ env_parallel env_parallel.bash env_parallel.zsh env_parallel.fish \
env_parallel.ksh env_parallel.pdksh env_parallel.csh env_parallel.tcsh env_parallel.ksh env_parallel.pdksh env_parallel.csh env_parallel.tcsh
@DOCUMENTATION_TRUE@man_MANS = parallel.1 env_parallel.1 sem.1 sql.1 niceload.1 \ @DOCUMENTATION_TRUE@man_MANS = parallel.1 env_parallel.1 sem.1 sql.1 niceload.1 \
@DOCUMENTATION_TRUE@ parallel_tutorial.7 parallel_design.7 @DOCUMENTATION_TRUE@ parallel_tutorial.7 parallel_design.7 parcat.1
@DOCUMENTATION_TRUE@doc_DATA = parallel.html env_parallel.html sem.html sql.html niceload.html \ @DOCUMENTATION_TRUE@doc_DATA = parallel.html env_parallel.html sem.html sql.html niceload.html \
@DOCUMENTATION_TRUE@ parallel_tutorial.html parallel_design.html \ @DOCUMENTATION_TRUE@ parallel_tutorial.html parallel_design.html parcat.html \
@DOCUMENTATION_TRUE@ parallel.texi env_parallel.texi sem.texi sql.texi niceload.texi \ @DOCUMENTATION_TRUE@ parallel.texi env_parallel.texi sem.texi sql.texi niceload.texi \
@DOCUMENTATION_TRUE@ parallel_tutorial.texi parallel_design.texi \ @DOCUMENTATION_TRUE@ parallel_tutorial.texi parallel_design.texi parcat.texi \
@DOCUMENTATION_TRUE@ parallel.pdf env_parallel.pdf sem.pdf sql.pdf niceload.pdf \ @DOCUMENTATION_TRUE@ parallel.pdf env_parallel.pdf sem.pdf sql.pdf niceload.pdf \
@DOCUMENTATION_TRUE@ parallel_tutorial.pdf parallel_design.pdf @DOCUMENTATION_TRUE@ parallel_tutorial.pdf parallel_design.pdf parcat.pdf
DISTCLEANFILES = parallel.1 env_parallel.1 sem.1 sql.1 niceload.1 \ DISTCLEANFILES = parallel.1 env_parallel.1 sem.1 sql.1 niceload.1 \
parallel_tutorial.7 parallel_design.7 \ parallel_tutorial.7 parallel_design.7 parcat.1 \
parallel.html env_parallel.html sem.html sql.html niceload.html \ parallel.html env_parallel.html sem.html sql.html niceload.html \
parallel_tutorial.html parallel_design.html \ parallel_tutorial.html parallel_design.html parcat.html \
parallel.texi env_parallel.texi sem.texi sql.texi niceload.texi \ parallel.texi env_parallel.texi sem.texi sql.texi niceload.texi \
parallel_tutorial.texi parallel_design.texi \ parallel_tutorial.texi parallel_design.texi parcat.texi \
parallel.pdf env_parallel.pdf sem.pdf sql.pdf niceload.pdf \ parallel.pdf env_parallel.pdf sem.pdf sql.pdf niceload.pdf \
parallel_tutorial.pdf parallel_design.pdf parallel_tutorial.pdf parallel_design.pdf parcat.pdf
EXTRA_DIST = parallel sem sql niceload env_parallel \ EXTRA_DIST = parallel sem sql niceload parcat env_parallel \
env_parallel.bash env_parallel.zsh env_parallel.fish env_parallel.ksh \ env_parallel.bash env_parallel.zsh env_parallel.fish env_parallel.ksh \
env_parallel.pdksh env_parallel.csh env_parallel.tcsh \ env_parallel.pdksh env_parallel.csh env_parallel.tcsh \
sem.pod parallel.pod env_parallel.pod niceload.pod parallel_tutorial.pod \ sem.pod parallel.pod env_parallel.pod niceload.pod parallel_tutorial.pod \
@ -644,6 +644,12 @@ niceload.1: niceload.pod
&& mv $(srcdir)/niceload.1n $(srcdir)/niceload.1 \ && mv $(srcdir)/niceload.1n $(srcdir)/niceload.1 \
|| echo "Warning: pod2man not found. Using old niceload.1" || echo "Warning: pod2man not found. Using old niceload.1"
parcat.1: parcat
pod2man --release='$(PACKAGE_VERSION)' --center='$(PACKAGE_NAME)' \
--section=1 $(srcdir)/parcat > $(srcdir)/parcat.1n \
&& mv $(srcdir)/parcat.1n $(srcdir)/parcat.1 \
|| echo "Warning: pod2man not found. Using old parcat.1"
parallel.html: parallel.pod parallel.html: parallel.pod
pod2html --title "GNU Parallel" $(srcdir)/parallel.pod > $(srcdir)/parallel.htmln \ pod2html --title "GNU Parallel" $(srcdir)/parallel.pod > $(srcdir)/parallel.htmln \
&& mv $(srcdir)/parallel.htmln $(srcdir)/parallel.html \ && mv $(srcdir)/parallel.htmln $(srcdir)/parallel.html \
@ -692,6 +698,13 @@ niceload.html: niceload.pod sql.html
|| echo "Warning: pod2html not found. Using old niceload.html" || echo "Warning: pod2html not found. Using old niceload.html"
rm -f $(srcdir)/pod2htm* rm -f $(srcdir)/pod2htm*
# Depending on niceload.html to avoid stupid pod2html race condition
parcat.html: parcat niceload.html
pod2html --title "GNU parcat" $(srcdir)/parcat > $(srcdir)/parcat.htmln \
&& mv $(srcdir)/parcat.htmln $(srcdir)/parcat.html \
|| echo "Warning: pod2html not found. Using old parcat.html"
rm -f $(srcdir)/pod2htm*
parallel.texi: parallel.pod parallel.texi: parallel.pod
pod2texi --output=$(srcdir)/parallel.texi $(srcdir)/parallel.pod \ pod2texi --output=$(srcdir)/parallel.texi $(srcdir)/parallel.pod \
|| echo "Warning: pod2texi not found. Using old parallel.texi" || echo "Warning: pod2texi not found. Using old parallel.texi"
@ -720,6 +733,10 @@ niceload.texi: niceload.pod
pod2texi --output=$(srcdir)/niceload.texi $(srcdir)/niceload.pod \ pod2texi --output=$(srcdir)/niceload.texi $(srcdir)/niceload.pod \
|| echo "Warning: pod2texi not found. Using old niceload.texi" || echo "Warning: pod2texi not found. Using old niceload.texi"
parcat.texi: parcat
pod2texi --output=$(srcdir)/parcat.texi $(srcdir)/parcat \
|| echo "Warning: pod2texi not found. Using old parcat.texi"
parallel.pdf: parallel.pod parallel.pdf: parallel.pod
pod2pdf --output-file $(srcdir)/parallel.pdf $(srcdir)/parallel.pod --title "GNU Parallel" \ pod2pdf --output-file $(srcdir)/parallel.pdf $(srcdir)/parallel.pod --title "GNU Parallel" \
|| echo "Warning: pod2pdf not found. Using old parallel.pdf" || echo "Warning: pod2pdf not found. Using old parallel.pdf"
@ -748,6 +765,10 @@ niceload.pdf: niceload.pod
pod2pdf --output-file $(srcdir)/niceload.pdf $(srcdir)/niceload.pod --title "GNU niceload" \ pod2pdf --output-file $(srcdir)/niceload.pdf $(srcdir)/niceload.pod --title "GNU niceload" \
|| echo "Warning: pod2pdf not found. Using old niceload.pdf" || echo "Warning: pod2pdf not found. Using old niceload.pdf"
parcat.pdf: parcat
pod2pdf --output-file $(srcdir)/parcat.pdf $(srcdir)/parcat --title "GNU parcat" \
|| echo "Warning: pod2pdf not found. Using old parcat.pdf"
sem: parallel sem: parallel
ln -fs parallel sem ln -fs parallel sem

View file

@ -86,9 +86,7 @@ B<Bash aliases>: Use B<env_parallel>.
B<Ksh functions and aliases>: Use B<env_parallel>. B<Ksh functions and aliases>: Use B<env_parallel>.
B<Zsh functions>: Use B<env_parallel>. B<Zsh functions and aliases>: Use B<env_parallel>.
B<Zsh aliases>: No solution.
B<Fish functions and aliases>: Use B<env_parallel>. B<Fish functions and aliases>: Use B<env_parallel>.
@ -96,7 +94,6 @@ B<Ksh functions and aliases>: Use B<env_parallel>.
B<Pdksh functions and aliases>: Use B<env_parallel>. B<Pdksh functions and aliases>: Use B<env_parallel>.
The command cannot contain the character \257 (macron: ¯). The command cannot contain the character \257 (macron: ¯).
=item B<{}> =item B<{}>
@ -254,7 +251,8 @@ Replace with calculated I<perl expression>. B<$_> will contain the
same as B<{}>. After evaluating I<perl expression> B<$_> will be used same as B<{}>. After evaluating I<perl expression> B<$_> will be used
as the value. It is recommended to only change $_ but you have full as the value. It is recommended to only change $_ but you have full
access to all of GNU B<parallel>'s internal functions and data access to all of GNU B<parallel>'s internal functions and data
structures. A few convenience functions have been made: structures. A few convenience functions and data structures have been
made:
=over 15 =over 15
@ -274,6 +272,10 @@ slot number of job
sequence number of job sequence number of job
=item Z<> B<@arg>
the arguments
=back =back
Example: Example:
@ -4805,19 +4807,19 @@ lacks many functions. All of these fail:
# -q to protect quoted $ and space # -q to protect quoted $ and space
parallel -q perl -e '$a=shift; print "$a"x10000000' ::: a b c parallel -q perl -e '$a=shift; print "$a"x10000000' ::: a b c
# Argument is command
parallel {} 10 ::: echo seq
# Generation of combination of inputs # Generation of combination of inputs
parallel echo {1} {2} ::: red green blue ::: S M L XL XXL parallel echo {1} {2} ::: red green blue ::: S M L XL XXL
# Composed commands
parallel echo {} '|' wc ::: a
# Output is mixed
parallel (-q) perl -e 'print"{}"x10000000' ::: a b c |
perl -pe '$_=join "\n",split //, $_' | uniq -c
# Show what would be executed # Show what would be executed
parallel --dry-run echo ::: a parallel --dry-run echo ::: a
# Run different shell dialects
zsh -c 'parallel echo \={} ::: zsh && true'
csh -c 'parallel echo \$\{\} ::: shell && true'
bash -c 'parallel echo \$\({}\) ::: pwd && true'
Rust parallel has no remote facilities. Rust parallel lacks B<::::>, B<--pipe>, and has no remote facilities.
Rust parallel buffers in RAM like gargs. This can cause
death-by-swapping. See B<man parallel_design>.
=head2 DIFFERENCES BETWEEN ClusterSSH AND GNU Parallel =head2 DIFFERENCES BETWEEN ClusterSSH AND GNU Parallel

View file

@ -904,6 +904,31 @@ Output:
pre-A-post pre-A-post
=head2 Respecting the shell
This tutorial uses Bash as the shell. GNU B<parallel> respects which
shell you are using, so in B<zsh> you can do:
parallel echo \={} ::: zsh bash ls
Output:
/usr/bin/zsh
/bin/bash
/bin/ls
In B<csh> you can do:
parallel 'set a="{}"; if( { test -d "$a" } ) echo "$a is a dir"' ::: *
Output:
[somedir] is a dir
This also becomes useful if you use GNU B<parallel> in a shell script:
GNU B<parallel> will use the same shell as the shell script.
=head1 Controlling the output =head1 Controlling the output
The output can prefixed with the argument: The output can prefixed with the argument:

303
src/parcat Normal file
View file

@ -0,0 +1,303 @@
#!/usr/bin/perl
=head1 NAME
parcat - cat files or fifos in parallel
=head1 SYNOPSIS
B<parcat> file(s)
=head1 DESCRIPTION
GNU B<parcat> reads files or fifos in parallel. It writes full lines
so there will be no problem with mixed-half-lines which you risk if
you use:
(cat file1 & cat file2 &) | ...
=head1 EXAMPLES
=head2 Do be done
mkfifo slot-{1..5}-digit-{0..9}
parallel -j5 'seq 100000 | grep {} > slot-{%}-digit-{}' ::: {0..9} &
parallel parcat slot-{1..5}-digit-{} '>' digit-{} ::: {0..9}
=head1 REPORTING BUGS
GNU B<parcat> is part of GNU B<parallel>. Report bugs to <bug-parallel@gnu.org>.
=head1 AUTHOR
When using GNU B<sql> for a publication please cite:
O. Tange (2011): GNU SQL - A Command Line Tool for Accessing Different
Databases Using DBURLs, ;login: The USENIX Magazine, April 2011:29-32.
Copyright (C) 2008,2009,2010 Ole Tange http://ole.tange.dk
Copyright (C) 2010,2011 Ole Tange, http://ole.tange.dk and Free
Software Foundation, Inc.
=head1 LICENSE
Copyright (C) 2007,2008,2009,2010,2011 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
at your option any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
=head2 Documentation license I
Permission is granted to copy, distribute and/or modify this documentation
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
Texts. A copy of the license is included in the file fdl.txt.
=head2 Documentation license II
You are free:
=over 9
=item B<to Share>
to copy, distribute and transmit the work
=item B<to Remix>
to adapt the work
=back
Under the following conditions:
=over 9
=item B<Attribution>
You must attribute the work in the manner specified by the author or
licensor (but not in any way that suggests that they endorse you or
your use of the work).
=item B<Share Alike>
If you alter, transform, or build upon this work, you may distribute
the resulting work only under the same, similar or a compatible
license.
=back
With the understanding that:
=over 9
=item B<Waiver>
Any of the above conditions can be waived if you get permission from
the copyright holder.
=item B<Public Domain>
Where the work or any of its elements is in the public domain under
applicable law, that status is in no way affected by the license.
=item B<Other Rights>
In no way are any of the following rights affected by the license:
=over 9
=item *
Your fair dealing or fair use rights, or other applicable
copyright exceptions and limitations;
=item *
The author's moral rights;
=item *
Rights other persons may have either in the work itself or in
how the work is used, such as publicity or privacy rights.
=back
=item B<Notice>
For any reuse or distribution, you must make clear to others the
license terms of this work.
=back
A copy of the full license is included in the file as cc-by-sa.txt.
=head1 DEPENDENCIES
GNU B<parcat> uses Perl.
=head1 SEE ALSO
B<cat>(1), B<parallel>(1)
=cut
use Symbol qw(gensym);
use IPC::Open3;
use POSIX qw(:errno_h);
use IO::Select;
use strict;
use threads;
use threads::shared;
use Thread::Queue;
my $opened :shared;
my $q = Thread::Queue->new();
my $okq = Thread::Queue->new();
my @producers;
if(not @ARGV) {
print "Usage:\n";
print " parcat file(s)\n";
}
for (@ARGV) {
push @producers, threads->create('producer', $_);
}
sub producer {
# Open a file/fifo, set non blocking, enqueue fileno of the file handle
my $file = shift;
open(my $fh, "<", $file) || do {
print STDERR "parcat: Cannot open $file\n";
exit(1);
};
set_fh_non_blocking($fh);
$q->enqueue(fileno($fh));
$opened++;
# Get an OK that the $fh is opened and we can release the $fh
while(1) {
my $ok = $okq->dequeue();
if($ok == fileno($fh)) { last; }
# Not ours - very unlikely to happen
$okq->enqueue($ok);
}
return;
}
my $s = IO::Select->new();
my %fhr;
my %buffer;
sub add_file {
my $fd = shift;
open(my $fh, "<&=", $fd) || die;
$s->add($fh);
$fhr{$fh}++;
# Tell the producer now opened here and can be released
$okq->enqueue($fd);
# Initialize the buffer
@{$buffer{$fh}} = ();
}
sub add_files {
# Non-blocking dequeue
while(defined(my $fd = $q->dequeue_nb())) {
add_file($fd);
}
}
sub add_files_block {
# Blocking dequeue
my $fd = $q->dequeue();
add_file($fd);
}
my $fd;
my (@ready,$file,$rv,$buf);
do {
# Wait until at least one file is opened
add_files_block();
while($q->pending or keys %fhr) {
add_files();
while(keys %fhr) {
@ready = $s->can_read(0.01);
if(not @ready) {
add_files();
}
for $file (@ready) {
$rv = sysread($file, $buf, 65536);
if (!$rv) {
if($! == EAGAIN) {
# Would block: Nothing read
next;
} else {
# This file is done
$s->remove($file);
delete $fhr{$file};
print @{$buffer{$file}};
delete $buffer{$file};
# Closing the $file causes it to block
# close $file;
add_files();
next;
}
}
# Find \n for full line
my $i = (rindex($buf,"\n")+1);
if($i) {
# Print full line
for(@{$buffer{$file}}, substr($buf,0,$i)) {
syswrite(STDOUT,$_);
}
# @buffer = remaining half line
@{$buffer{$file}} = (substr($buf,$i,$rv-$i));
redo;
} else {
# Something read, but not a full line
push @{$buffer{$file}}, $buf;
redo;
}
}
}
}
} while($opened <= $#ARGV);
for (@producers) {
$_->join();
}
sub set_fh_non_blocking {
# Set filehandle as non-blocking
# Inputs:
# $fh = filehandle to be blocking
# Returns:
# N/A
my $fh = shift;
$Global::use{"Fcntl"} ||= eval "use Fcntl qw(:DEFAULT :flock); 1;";
my $flags;
fcntl($fh, &F_GETFL, $flags) || die $!; # Get the current flags on the filehandle
$flags |= &O_NONBLOCK; # Add non-blocking to the flags
fcntl($fh, &F_SETFL, $flags) || die $!; # Set the flags on the filehandle
}