parallel: Kill children if receving TERM or INT.

This commit is contained in:
Ole Tange 2015-04-10 01:20:51 +02:00
parent 98c4da806f
commit d8ec46d2d8
16 changed files with 539 additions and 389 deletions

4
NEWS
View file

@ -105,10 +105,6 @@ New in this release:
Performance Stream Parallel Processing Performance Stream Parallel Processing
http://www.cs.wustl.edu/~lip/pubs/pmam15_jbeard.pdf http://www.cs.wustl.edu/~lip/pubs/pmam15_jbeard.pdf
* GNU Parallel was cited in: Towards Collaborative Exploration and
Analysis of Big Data from Mars: A Noachis Terra Case Study
http://link.springer.com/chapter/10.1007/978-3-319-13865-7_25
* GNU Parallel was cited in: Quantifying properties of hot and dense * GNU Parallel was cited in: Quantifying properties of hot and dense
QCD matter through systematic model-to-data comparison QCD matter through systematic model-to-data comparison
http://arxiv.org/pdf/1502.00339.pdf http://arxiv.org/pdf/1502.00339.pdf

View file

@ -209,9 +209,9 @@ cc:Tim Cuthbertson <tim3d.junk@gmail.com>,
Ryoichiro Suzuki <ryoichiro.suzuki@gmail.com>, Ryoichiro Suzuki <ryoichiro.suzuki@gmail.com>,
Jesse Alama <jesse.alama@gmail.com> Jesse Alama <jesse.alama@gmail.com>
Subject: GNU Parallel 20150422 ('') released Subject: GNU Parallel 20150422 ('Germanwings') released
GNU Parallel 20150422 ('') has been released. It is available for download at: http://ftp.gnu.org/gnu/parallel/ GNU Parallel 20150422 ('Germanwings') has been released. It is available for download at: http://ftp.gnu.org/gnu/parallel/
Haiku of the month: Haiku of the month:
@ -221,6 +221,8 @@ New in this release:
* GNU Parallel now has a DOI: https://dx.doi.org/10.5281/zenodo.16303 * GNU Parallel now has a DOI: https://dx.doi.org/10.5281/zenodo.16303
* GNU Parallel was cited in: Scaling Machine Learning for Target Prediction in Drug Discovery using Apache Spark https://cris.cumulus.vub.ac.be/portal/files/5147244/spark.pdf
* GNU Parallel was cited in: Bayesian inference of protein structure from chemical shift data https://peerj.com/articles/861/ * GNU Parallel was cited in: Bayesian inference of protein structure from chemical shift data https://peerj.com/articles/861/
* GNU Parallel was cited in: Toward Enhanced Metadata Quality of Large-Scale Digital Libraries: Estimating Volume Time Range https://www.ideals.illinois.edu/bitstream/handle/2142/73656/186_ready.pdf * GNU Parallel was cited in: Toward Enhanced Metadata Quality of Large-Scale Digital Libraries: Estimating Volume Time Range https://www.ideals.illinois.edu/bitstream/handle/2142/73656/186_ready.pdf
@ -255,6 +257,8 @@ taxator-tk http://algbio.cs.uni-duesseldorf.de/webapps/wa-download/ (check it)
* GNU Parallel (Sebuah Uji Coba) http://kaka.prakasa.my.id/2014/09/04/gnu-parallel-sebuah-uji-coba/ * GNU Parallel (Sebuah Uji Coba) http://kaka.prakasa.my.id/2014/09/04/gnu-parallel-sebuah-uji-coba/
* 你见过的最牛逼的命令行程序是什么? http://www.zhihu.com/question/29257300
* Bug fixes and man page updates. * Bug fixes and man page updates.
GNU Parallel - For people who live life in the parallel lane. GNU Parallel - For people who live life in the parallel lane.

View file

@ -49,29 +49,28 @@ if($Global::max_number_of_args) {
my @command = @ARGV; my @command = @ARGV;
my @fhlist; my @input_source_fh;
if($opt::pipepart) { if($opt::pipepart) {
@fhlist = map { open_or_exit($_) } "/dev/null"; @input_source_fh = map { open_or_exit($_) } "/dev/null";
} else { } else {
@fhlist = map { open_or_exit($_) } @opt::a; @input_source_fh = map { open_or_exit($_) } @opt::a;
if(not @fhlist and not $opt::pipe) { if(not @input_source_fh and not $opt::pipe) {
@fhlist = (*STDIN); @input_source_fh = (*STDIN);
} }
} }
if($opt::skip_first_line) { if($opt::skip_first_line) {
# Skip the first line for the first file handle # Skip the first line for the first file handle
my $fh = $fhlist[0]; my $fh = $input_source_fh[0];
<$fh>; <$fh>;
} }
if($opt::header and not $opt::pipe) { if($opt::header and not $opt::pipe) {
my $fh = $fhlist[0];
# split with colsep or \t # split with colsep or \t
# $header force $colsep = \t if undef? # $header force $colsep = \t if undef?
my $delimiter = $opt::colsep; my $delimiter = $opt::colsep;
$delimiter ||= "\$"; $delimiter ||= "\t";
my $id = 1; my $id = 1;
for my $fh (@fhlist) { for my $fh (@input_source_fh) {
my $line = <$fh>; my $line = <$fh>;
chomp($line); chomp($line);
::debug("init", "Delimiter: '$delimiter'"); ::debug("init", "Delimiter: '$delimiter'");
@ -89,7 +88,7 @@ if($opt::header and not $opt::pipe) {
} }
} else { } else {
my $id = 1; my $id = 1;
for my $fh (@fhlist) { for my $fh (@input_source_fh) {
$Global::input_source_header{$id} = $id; $Global::input_source_header{$id} = $id;
$id++; $id++;
} }
@ -101,7 +100,7 @@ if($opt::filter_hosts and (@opt::sshlogin or @opt::sshloginfile)) {
} }
if($opt::nonall or $opt::onall) { if($opt::nonall or $opt::onall) {
onall(@command); onall(\@input_source_fh,@command);
wait_and_exit(min(undef_as_zero($Global::exitstatus),254)); wait_and_exit(min(undef_as_zero($Global::exitstatus),254));
} }
@ -109,7 +108,7 @@ if($opt::nonall or $opt::onall) {
# multiple --transfer and --basefile with different /./ # multiple --transfer and --basefile with different /./
$Global::JobQueue = JobQueue->new( $Global::JobQueue = JobQueue->new(
\@command,\@fhlist,$Global::ContextReplace,$number_of_args,\@Global::ret_files); \@command,\@input_source_fh,$Global::ContextReplace,$number_of_args,\@Global::ret_files);
if($opt::eta or $opt::bar) { if($opt::eta or $opt::bar) {
# Count the number of jobs before starting any # Count the number of jobs before starting any
@ -1052,7 +1051,7 @@ sub parse_options {
sub init_globals { sub init_globals {
# Defaults: # Defaults:
$Global::version = 20150329; $Global::version = 20150403;
$Global::progname = 'parallel'; $Global::progname = 'parallel';
$Global::infinity = 2**31; $Global::infinity = 2**31;
$Global::debug = 0; $Global::debug = 0;
@ -1086,7 +1085,6 @@ sub init_globals {
); );
# Modifiable copy of %Global::replace # Modifiable copy of %Global::replace
%Global::rpl = %Global::replace; %Global::rpl = %Global::replace;
$Global::parens = "{==}";
$/ = "\n"; $/ = "\n";
$Global::ignore_empty = 0; $Global::ignore_empty = 0;
$Global::interactive = 0; $Global::interactive = 0;
@ -1109,6 +1107,24 @@ sub init_globals {
sub parse_replacement_string_options { sub parse_replacement_string_options {
# Deal with --rpl # Deal with --rpl
# Uses:
# %Global::rpl
# $Global::parensleft
# $Global::parensright
# $opt::parens
# $Global::parensleft
# $Global::parensright
# $opt::plus
# %Global::plus
# $opt::I
# $opt::U
# $opt::i
# $opt::basenamereplace
# $opt::dirnamereplace
# $opt::seqreplace
# $opt::slotreplace
# $opt::basenameextensionreplace
sub rpl { sub rpl {
# Modify %Global::rpl # Modify %Global::rpl
# Replace $old with $new # Replace $old with $new
@ -1118,10 +1134,11 @@ sub parse_replacement_string_options {
delete $Global::rpl{$old}; delete $Global::rpl{$old};
} }
} }
if(defined $opt::parens) { $Global::parens = $opt::parens; } my $parens = "{==}";
my $parenslen = 0.5*length $Global::parens; if(defined $opt::parens) { $parens = $opt::parens; }
$Global::parensleft = substr($Global::parens,0,$parenslen); my $parenslen = 0.5*length $parens;
$Global::parensright = substr($Global::parens,$parenslen); $Global::parensleft = substr($parens,0,$parenslen);
$Global::parensright = substr($parens,$parenslen);
if(defined $opt::plus) { %Global::rpl = (%Global::plus,%Global::rpl); } if(defined $opt::plus) { %Global::rpl = (%Global::plus,%Global::rpl); }
if(defined $opt::I) { rpl('{}',$opt::I); } if(defined $opt::I) { rpl('{}',$opt::I); }
if(defined $opt::U) { rpl('{.}',$opt::U); } if(defined $opt::U) { rpl('{.}',$opt::U); }
@ -1145,6 +1162,23 @@ sub parse_semaphore {
# Semaphore defaults # Semaphore defaults
# Must be done before computing number of processes and max_line_length # Must be done before computing number of processes and max_line_length
# because when running as a semaphore GNU Parallel does not read args # because when running as a semaphore GNU Parallel does not read args
# Uses:
# $opt::semaphore
# $Global::semaphore
# $opt::semaphoretimeout
# $Semaphore::timeout
# $opt::semaphorename
# $Semaphore::name
# $opt::fg
# $Semaphore::fg
# $opt::wait
# $Semaphore::wait
# $opt::bg
# @opt::a
# @Global::unget_argv
# $Global::default_simultaneous_sshlogins
# $opt::jobs
# $Global::interactive
$Global::semaphore ||= ($0 =~ m:(^|/)sem$:); # called as 'sem' $Global::semaphore ||= ($0 =~ m:(^|/)sem$:); # called as 'sem'
if(defined $opt::semaphore) { $Global::semaphore = 1; } if(defined $opt::semaphore) { $Global::semaphore = 1; }
if(defined $opt::semaphoretimeout) { $Global::semaphore = 1; } if(defined $opt::semaphoretimeout) { $Global::semaphore = 1; }
@ -1177,18 +1211,6 @@ sub parse_semaphore {
} }
} }
sub env_quote {
# Input:
# $v = value to quote
# Returns:
# $v = value quoted as environment variable
my $v = $_[0];
$v =~ s/([\\])/\\$1/g;
$v =~ s/([\[\] \#\'\&\<\>\(\)\;\{\}\t\"\$\`\*\174\!\?\~])/\\$1/g;
$v =~ s/\n/"\n"/g;
return $v;
}
sub record_env { sub record_env {
# Record current %ENV-keys in ~/.parallel/ignored_vars # Record current %ENV-keys in ~/.parallel/ignored_vars
# Returns: N/A # Returns: N/A
@ -1495,8 +1517,8 @@ sub read_args_from_command_line {
sub cleanup { sub cleanup {
# Returns: N/A # Returns: N/A
if(@opt::basefile) { cleanup_basefile(); }
unlink keys %Global::unlink; unlink keys %Global::unlink;
if(@opt::basefile) { cleanup_basefile(); }
} }
sub __QUOTING_ARGUMENTS_FOR_SHELL__ {} sub __QUOTING_ARGUMENTS_FOR_SHELL__ {}
@ -2798,14 +2820,14 @@ sub onall {
close $fh; close $fh;
return $tmpfile; return $tmpfile;
} }
my @command = @_; my ($input_source_fh_ref,@command) = @_;
if($Global::quoting) { if($Global::quoting) {
@command = shell_quote_empty(@command); @command = shell_quote_empty(@command);
} }
# Copy all @fhlist (-a and :::) into tempfiles # Copy all @input_source_fh (-a and :::) into tempfiles
my @argfiles = (); my @argfiles = ();
for my $fh (@fhlist) { for my $fh (@$input_source_fh_ref) {
my ($outfh, $name) = ::tmpfile(SUFFIX => ".all", UNLINK => 1); my ($outfh, $name) = ::tmpfile(SUFFIX => ".all", UNLINK => 1);
print $outfh (<$fh>); print $outfh (<$fh>);
close $outfh; close $outfh;
@ -2893,10 +2915,14 @@ sub save_original_signal_handler {
# Uses: # Uses:
# %Global::original_sig # %Global::original_sig
# Returns: N/A # Returns: N/A
$SIG{INT} = sub { if($opt::tmux) { qx { tmux kill-session -t p$$ }; } $SIG{INT} = sub {
unlink keys %Global::unlink; exit -1 }; if($opt::tmux) { qx { tmux kill-session -t p$$ }; }
$SIG{TERM} = sub { if($opt::tmux) { qx { tmux kill-session -t p$$ }; } wait_and_exit(255);
unlink keys %Global::unlink; exit -1 }; };
$SIG{TERM} = sub {
if($opt::tmux) { qx { tmux kill-session -t p$$ }; }
wait_and_exit(255);
};
%Global::original_sig = %SIG; %Global::original_sig = %SIG;
$SIG{TERM} = sub {}; # Dummy until jobs really start $SIG{TERM} = sub {}; # Dummy until jobs really start
$SIG{ALRM} = 'IGNORE'; $SIG{ALRM} = 'IGNORE';
@ -3009,11 +3035,11 @@ sub wait_and_exit {
# If we do not wait, we sometimes get segfault # If we do not wait, we sometimes get segfault
# Returns: N/A # Returns: N/A
my $error = shift; my $error = shift;
unlink keys %Global::unlink;
if($error) { if($error) {
# Kill all without printing # Kill all without printing
for my $job (values %Global::running) { for my $job (values %Global::running) {
$job->kill("TERM"); $job->kill();
$job->kill("TERM");
} }
} }
for (keys %Global::unkilled_children) { for (keys %Global::unkilled_children) {
@ -3193,10 +3219,16 @@ sub bibtex {
if(open (my $fh, ">", $ENV{'HOME'}."/.parallel/will-cite")) { if(open (my $fh, ">", $ENV{'HOME'}."/.parallel/will-cite")) {
close $fh; close $fh;
print "\nThank you for your support. It is much appreciated. The citation\n", print "\nThank you for your support. It is much appreciated. The citation\n",
"notice is now silenced. You may also use '--will-cite'.\n"; "notice is now silenced. You may also use '--will-cite'.\n",
"If you use '--will-cite' in scripts you are expected to pay\n",
"the 10000 EUR, because you are making it harder to see the\n",
"citation notice.\n\n";
} else { } else {
print "\nThank you for your support. It is much appreciated. The citation\n", print "\nThank you for your support. It is much appreciated. The citation\n",
"cannot permanently be disabled. Use --will-cite instead.\n"; "cannot permanently be silenced. Use '--will-cite' instead.\n",
"If you use '--will-cite' in scripts you are expected to pay\n",
"the 10000 EUR, because you are making it harder to see the\n",
"citation notice.\n\n";
last; last;
} }
} }
@ -5637,12 +5669,13 @@ sub empty_input_detector {
# If some input: Pass input as input to pipe # If some input: Pass input as input to pipe
# This avoids starting the $read command if there is no input. # This avoids starting the $read command if there is no input.
# Returns: # Returns:
# $cmd = script to prepend to '($real command)' # $cmd = script to prepend to '| ($real command)'
# The $tmpfile might exist if run on a remote system - we accept that risk # The $tmpfile might exist if run on a remote system - we accept that risk
my ($dummy_fh, $tmpfile) = ::tmpfile(SUFFIX => ".chr"); my ($dummy_fh, $tmpfile) = ::tmpfile(SUFFIX => ".chr");
# Unlink to avoid leaving files if --dry-run or --sshlogin # Unlink to avoid leaving files if --dry-run or --sshlogin
unlink $tmpfile; unlink $tmpfile;
$Global::unlink{$tmpfile} = 1;
my $cmd = my $cmd =
# Exit value: # Exit value:
# empty input = true # empty input = true
@ -5650,7 +5683,7 @@ sub empty_input_detector {
# sh -c needed as csh cannot hide stderr # sh -c needed as csh cannot hide stderr
qq{ sh -c 'dd bs=1 count=1 of=$tmpfile 2>/dev/null'; }. qq{ sh -c 'dd bs=1 count=1 of=$tmpfile 2>/dev/null'; }.
qq{ test \! -s "$tmpfile" && rm -f "$tmpfile" && exec true; }. qq{ test \! -s "$tmpfile" && rm -f "$tmpfile" && exec true; }.
qq{ (cat $tmpfile; rm $tmpfile; cat - ) | }; qq{ (cat $tmpfile; rm $tmpfile; cat - ) };
return $cmd; return $cmd;
} }
@ -5663,7 +5696,7 @@ sub filter_through_compress {
for my $fdno (1,2) { for my $fdno (1,2) {
my $wpid = open(my $fdw,"|-", empty_input_detector(). my $wpid = open(my $fdw,"|-", empty_input_detector().
"($opt::compress_program) >>". "| ($opt::compress_program) >>".
$self->fh($fdno,'name')) || die $?; $self->fh($fdno,'name')) || die $?;
$self->set_fh($fdno,'w',$fdw); $self->set_fh($fdno,'w',$fdw);
$self->set_fh($fdno,'wpid',$wpid); $self->set_fh($fdno,'wpid',$wpid);
@ -5890,7 +5923,7 @@ sub kill {
# Record this jobs as failed # Record this jobs as failed
$self->set_exitstatus(-1); $self->set_exitstatus(-1);
# Send two TERMs to give time to clean up # Send two TERMs to give time to clean up
::debug("run", "Kill seq ", $self->seq(), "\n"); ::debug("run", "Kill seq ", $self->seq(), " signal '@signals'\n");
my @send_signals = @signals || ("TERM", "TERM", "KILL"); my @send_signals = @signals || ("TERM", "TERM", "KILL");
for my $signal (@send_signals) { for my $signal (@send_signals) {
my $alive = 0; my $alive = 0;
@ -5899,6 +5932,7 @@ sub kill {
# The job still running # The job still running
kill $signal, $pid; kill $signal, $pid;
$alive = 1; $alive = 1;
::debug("run","$pid is alive\n");
} }
} }
# If a signal was given as input, do not do the sleep below # If a signal was given as input, do not do the sleep below
@ -6119,7 +6153,7 @@ sub wrapped {
$command = (shift @Global::cat_partials). " | ($command)"; $command = (shift @Global::cat_partials). " | ($command)";
} elsif($opt::pipe) { } elsif($opt::pipe) {
# Prepend EOF-detector to avoid starting $command if EOF. # Prepend EOF-detector to avoid starting $command if EOF.
$command = empty_input_detector(). "($command);"; $command = empty_input_detector(). "| ($command);";
} }
if($opt::tmux) { if($opt::tmux) {
# Wrap command with 'tmux' # Wrap command with 'tmux'
@ -6761,7 +6795,7 @@ sub print_dryrun_and_verbose {
my $actual_command = shift; my $actual_command = shift;
# Temporary file name. Used for fifo to communicate exit val # Temporary file name. Used for fifo to communicate exit val
my ($fh, $tmpfile) = ::tmpfile(SUFFIX => ".tmx"); my ($fh, $tmpfile) = ::tmpfile(SUFFIX => ".tmx");
$Global::unlink{$tmpfile}=1; $Global::unlink{$tmpfile} = 1;
close $fh; close $fh;
unlink $tmpfile; unlink $tmpfile;
my $visual_command = $self->replaced(); my $visual_command = $self->replaced();
@ -8213,11 +8247,11 @@ sub tmux_length {
for my $l (1, 2020, 16320, 100000, $len) { for my $l (1, 2020, 16320, 100000, $len) {
my ($fh, $tmpfile) = ::tmpfile(SUFFIX => ".tmb"); my ($fh, $tmpfile) = ::tmpfile(SUFFIX => ".tmb");
close $fh; close $fh;
$Global::unlink{$tmpfile} = 1;
unlink $tmpfile; unlink $tmpfile;
my $tmuxcmd = "sh -c '".$ENV{'TMUX'}." -S $tmpfile new-session -d -n echo $l". my $tmuxcmd = "sh -c '".$ENV{'TMUX'}." -S $tmpfile new-session -d -n echo $l".
("x"x$l). " 2>/dev/null' && echo $l"; ("x"x$l). " 2>/dev/null' && echo $l; rm -f $tmpfile";
push @out, qx{ $tmuxcmd }; push @out, qx{ $tmuxcmd };
unlink $tmpfile;
} }
::debug("tmux","tmux-length ",@out); ::debug("tmux","tmux-length ",@out);
chomp @out; chomp @out;

View file

@ -32,7 +32,8 @@ is CentOS 3.9 and Perl 5.8.0.
GNU B<parallel> busy waits. This is because the reason why a job is GNU B<parallel> busy waits. This is because the reason why a job is
not started may be due to load average, and thus it will not make not started may be due to load average, and thus it will not make
sense to wait for a job to finish. Instead the load average must be sense to wait for a job to finish. Instead the load average must be
checked again. Load average is not the only reason. checked again. Load average is not the only reason: --timeout has a
similar problem.
To not burn up too up too much CPU GNU B<parallel> sleeps To not burn up too up too much CPU GNU B<parallel> sleeps
exponentially longer and longer if nothing happens, maxing out at 1 exponentially longer and longer if nothing happens, maxing out at 1
@ -282,17 +283,17 @@ too. Take B<--nice> as an example:
will work just fine. But when run remotely, you need to move the nice will work just fine. But when run remotely, you need to move the nice
command so it is being run on the server: command so it is being run on the server:
parallel --S server nice command ... parallel -S server nice command ...
And this will again work just fine, as long as you are running a And this will again work just fine, as long as you are running a
single command. When you are running a composed command you need nice single command. When you are running a composed command you need nice
to apply to the whole command, and it gets harder still: to apply to the whole command, and it gets harder still:
parallel --S server --q nice bash -c 'command1 ...; command2 | command3' parallel -S server -q nice bash -c 'command1 ...; command2 | command3'
It is not impossible, but by using B<--nice> GNU B<parallel> will do It is not impossible, but by using B<--nice> GNU B<parallel> will do
the right thing for you. Similarly when transferring files: It starts the right thing for you. Similarly when transferring files: It starts
to get hard when the file names contain space, :, , *, or other to get hard when the file names contain space, :, `, *, or other
special characters. special characters.
To run the commands in a B<tmux> session you basically just need to To run the commands in a B<tmux> session you basically just need to
@ -324,7 +325,7 @@ post-shellshock versions of B<bash>.
=head2 Remote Ctrl-C and standard error (stderr) =head2 Remote Ctrl-C and standard error (stderr)
If the user presses Ctrl-C the user expect jobs to stop. This works If the user presses Ctrl-C the user expects jobs to stop. This works
out of the box if the jobs are run locally. Unfortunately it is not so out of the box if the jobs are run locally. Unfortunately it is not so
simple if the jobs are run remotely. simple if the jobs are run remotely.
@ -366,7 +367,7 @@ stderr. The wrapper looks like this:
=head2 Transferring of variables and functions =head2 Transferring of variables and functions
Transferring of variables and functions given by B<-env> is done by Transferring of variables and functions given by B<--env> is done by
running a Perl script remotely that calls the actual command. The Perl running a Perl script remotely that calls the actual command. The Perl
script sets $ENV{variable} to the correct value before exec'ing the a script sets $ENV{variable} to the correct value before exec'ing the a
shell that runs the function definition followed by the actual shell that runs the function definition followed by the actual
@ -378,7 +379,7 @@ B<parallel_bash_environment>. This variable is picked up by GNU
B<parallel> and used to create the Perl script mentioned above. B<parallel> and used to create the Perl script mentioned above.
=head2 Base64 encode bzip2 =head2 Base64 encoded bzip2
B<csh> limits words of commands to 1024 chars. This is often too little B<csh> limits words of commands to 1024 chars. This is often too little
when GNU B<parallel> encodes environment variables and wraps the when GNU B<parallel> encodes environment variables and wraps the

View file

@ -553,27 +553,27 @@
<p>When predefined replacement strings are not flexible enough a perl expression can be used instead. One example is to remove two extensions: foo.tar.gz -&gt; foo</p> <p>When predefined replacement strings are not flexible enough a perl expression can be used instead. One example is to remove two extensions: foo.tar.gz -&gt; foo</p>
<pre><code> parallel echo &#39;{= s:\.[^.]+$::;s:\.[^.]+$::; =}&#39; ::: foo.tar.gz</code></pre> <pre><code> parallel echo &#39;{= s:\.[^.]+$::;s:\.[^.]+$::; =}&#39; ::: foo.tar.gz</code></pre>
<p>Output:</p> <p>Output:</p>
<pre><code> foo</code></pre> <pre><code> foo</code></pre>
<p>If the strings <b>{=</b> and <b>=}</b> cause problems they can be replaced with --parens:</p> <p>If the strings <b>{=</b> and <b>=}</b> cause problems they can be replaced with --parens:</p>
<pre><code> parallel --parens ,,,, echo &#39;,, s:\.[^.]+$::;s:\.[^.]+$::; ,,&#39; ::: foo.tar.gz</code></pre> <pre><code> parallel --parens ,,,, echo &#39;,, s:\.[^.]+$::;s:\.[^.]+$::; ,,&#39; ::: foo.tar.gz</code></pre>
<p>Output: Same as above.</p> <p>Output: Same as above.</p>
<p>To define a short hand replacement string use <b>--rpl</b>:</p> <p>To define a short hand replacement string use <b>--rpl</b>:</p>
<pre><code> parallel --rpl &#39;.. s:\.[^.]+$::;s:\.[^.]+$::;&#39; echo &#39;..&#39; ::: foo.tar.gz</code></pre> <pre><code> parallel --rpl &#39;.. s:\.[^.]+$::;s:\.[^.]+$::;&#39; echo &#39;..&#39; ::: foo.tar.gz</code></pre>
<p>Output: Same as above.</p> <p>Output: Same as above.</p>
<p>If the short hand starts with &#39;{&#39; it can be used as a positional replacement string, too:</p> <p>If the short hand starts with &#39;{&#39; it can be used as a positional replacement string, too:</p>
<pre><code> parallel --rpl &#39;{..} s:\.[^.]+$::;s:\.[^.]+$::;&#39; echo &#39;{..}&#39; ::: foo.tar.gz</code></pre> <pre><code> parallel --rpl &#39;{..} s:\.[^.]+$::;s:\.[^.]+$::;&#39; echo &#39;{..}&#39; ::: foo.tar.gz</code></pre>
<p>Output: Same as above.</p> <p>Output: Same as above.</p>
@ -628,15 +628,15 @@
<p>To use a perl expression as a positional replacement string simply prepend the perl expression with number and space:</p> <p>To use a perl expression as a positional replacement string simply prepend the perl expression with number and space:</p>
<pre><code> parallel echo &#39;{=2 s:\.[^.]+$::;s:\.[^.]+$::; =} {1}&#39; ::: bar ::: foo.tar.gz</code></pre> <pre><code> parallel echo &#39;{=2 s:\.[^.]+$::;s:\.[^.]+$::; =} {1}&#39; ::: bar ::: foo.tar.gz</code></pre>
<p>Output:</p> <p>Output:</p>
<pre><code> foo bar</code></pre> <pre><code> foo bar</code></pre>
<p>If a defined short hand starts with &#39;{&#39; it can be used as a positional replacement string, too:</p> <p>If a defined short hand starts with &#39;{&#39; it can be used as a positional replacement string, too:</p>
<pre><code> parallel --rpl &#39;{..} s:\.[^.]+$::;s:\.[^.]+$::;&#39; echo &#39;{2..} {1}&#39; ::: bar ::: foo.tar.gz</code></pre> <pre><code> parallel --rpl &#39;{..} s:\.[^.]+$::;s:\.[^.]+$::;&#39; echo &#39;{2..} {1}&#39; ::: bar ::: foo.tar.gz</code></pre>
<p>Output: Same as above.</p> <p>Output: Same as above.</p>
@ -918,7 +918,7 @@
<p>To get the output immediately use --ungroup:</p> <p>To get the output immediately use --ungroup:</p>
<pre><code> parallel -j2 --ungroup &#39;printf &quot;%s-start\n%s&quot; {} {};sleep {};printf &quot;%s\n&quot; -middle;echo {}-end&#39; ::: 4 2 1 </code></pre> <pre><code> parallel -j2 --ungroup &#39;printf &quot;%s-start\n%s&quot; {} {};sleep {};printf &quot;%s\n&quot; -middle;echo {}-end&#39; ::: 4 2 1</code></pre>
<p>Output:</p> <p>Output:</p>
@ -1117,14 +1117,14 @@
Starting 3 Starting 3
Thu Aug 15 16:24:38 CEST 2013</code></pre> Thu Aug 15 16:24:38 CEST 2013</code></pre>
<p>If jobs taking more than a certain amount of time are known to fail, they can be stopped with --timeout:</p> <p>If jobs taking more than a certain amount of time are known to fail, they can be stopped with --timeout. The accuracy of --timeout is 2 seconds:</p>
<pre><code> parallel --timeout 2.1 sleep {}\; echo {} ::: 1 2 3 4</code></pre> <pre><code> parallel --timeout 4.1 sleep {}\; echo {} ::: 2 4 6 8</code></pre>
<p>Output:</p> <p>Output:</p>
<pre><code> 1 <pre><code> 2
2</code></pre> 4</code></pre>
<p>GNU Parallel can compute the median runtime for jobs and kill those that take more than 200% of the median runtime:</p> <p>GNU Parallel can compute the median runtime for jobs and kill those that take more than 200% of the median runtime:</p>

View file

@ -452,28 +452,28 @@ When predefined replacement strings are not flexible enough a perl
expression can be used instead. One example is to remove two expression can be used instead. One example is to remove two
extensions: foo.tar.gz -> foo extensions: foo.tar.gz -> foo
parallel echo '{= s:\.[^.]+$::;s:\.[^.]+$::; =}' ::: foo.tar.gz parallel echo '{= s:\.[^.]+$::;s:\.[^.]+$::; =}' ::: foo.tar.gz
Output: Output:
foo foo
If the strings B<{=> and B<=}> cause problems they can be replaced with --parens: If the strings B<{=> and B<=}> cause problems they can be replaced with --parens:
parallel --parens ,,,, echo ',, s:\.[^.]+$::;s:\.[^.]+$::; ,,' ::: foo.tar.gz parallel --parens ,,,, echo ',, s:\.[^.]+$::;s:\.[^.]+$::; ,,' ::: foo.tar.gz
Output: Same as above. Output: Same as above.
To define a short hand replacement string use B<--rpl>: To define a short hand replacement string use B<--rpl>:
parallel --rpl '.. s:\.[^.]+$::;s:\.[^.]+$::;' echo '..' ::: foo.tar.gz parallel --rpl '.. s:\.[^.]+$::;s:\.[^.]+$::;' echo '..' ::: foo.tar.gz
Output: Same as above. Output: Same as above.
If the short hand starts with '{' it can be used as a positional If the short hand starts with '{' it can be used as a positional
replacement string, too: replacement string, too:
parallel --rpl '{..} s:\.[^.]+$::;s:\.[^.]+$::;' echo '{..}' ::: foo.tar.gz parallel --rpl '{..} s:\.[^.]+$::;s:\.[^.]+$::;' echo '{..}' ::: foo.tar.gz
Output: Same as above. Output: Same as above.
@ -532,16 +532,16 @@ Output (the order may be different):
To use a perl expression as a positional replacement string simply To use a perl expression as a positional replacement string simply
prepend the perl expression with number and space: prepend the perl expression with number and space:
parallel echo '{=2 s:\.[^.]+$::;s:\.[^.]+$::; =} {1}' ::: bar ::: foo.tar.gz parallel echo '{=2 s:\.[^.]+$::;s:\.[^.]+$::; =} {1}' ::: bar ::: foo.tar.gz
Output: Output:
foo bar foo bar
If a defined short hand starts with '{' it can be used as a positional If a defined short hand starts with '{' it can be used as a positional
replacement string, too: replacement string, too:
parallel --rpl '{..} s:\.[^.]+$::;s:\.[^.]+$::;' echo '{2..} {1}' ::: bar ::: foo.tar.gz parallel --rpl '{..} s:\.[^.]+$::;s:\.[^.]+$::;' echo '{2..} {1}' ::: bar ::: foo.tar.gz
Output: Same as above. Output: Same as above.
@ -838,10 +838,10 @@ Output:
To get the output immediately use --ungroup: To get the output immediately use --ungroup:
parallel -j2 --ungroup 'printf "%s-start\n%s" {} {};sleep {};printf "%s\n" -middle;echo {}-end' ::: 4 2 1 parallel -j2 --ungroup 'printf "%s-start\n%s" {} {};sleep {};printf "%s\n" -middle;echo {}-end' ::: 4 2 1
Output: Output:
4-start 4-start
42-start 42-start
2-middle 2-middle
@ -1052,14 +1052,15 @@ Output:
If jobs taking more than a certain amount of time are known to fail, If jobs taking more than a certain amount of time are known to fail,
they can be stopped with --timeout: they can be stopped with --timeout. The accuracy of --timeout is 2
seconds:
parallel --timeout 2.1 sleep {}\; echo {} ::: 1 2 3 4 parallel --timeout 4.1 sleep {}\; echo {} ::: 2 4 6 8
Output: Output:
1
2 2
4
GNU Parallel can compute the median runtime for jobs and kill those GNU Parallel can compute the median runtime for jobs and kill those
that take more than 200% of the median runtime: that take more than 200% of the median runtime:
@ -1484,7 +1485,7 @@ arguments. --nonall is a no argument --onall:
parallel --nonall -S $SERVER1,$SERVER2 echo foo bar parallel --nonall -S $SERVER1,$SERVER2 echo foo bar
Output: Output:
foo bar foo bar
foo bar foo bar

View file

@ -2,28 +2,28 @@ testsuite: 3
true true
3: ../src/parallel tests-to-run/* wanted-results/* startdb prereqlocal prereqremote 3: ../src/parallel tests-to-run/* wanted-results/* startdb prereqlocal prereqremote
TRIES=3 time sh Start.sh - mem || true TRIES=3 time bash Start.sh - mem || true
touch ~/.parallel/will-cite touch ~/.parallel/will-cite
make stopvm make stopvm
1: ../src/parallel tests-to-run/* wanted-results/* prereqlocal startdb prereqremote 1: ../src/parallel tests-to-run/* wanted-results/* prereqlocal startdb prereqremote
time sh Start.sh - mem || true time bash Start.sh - mem || true
touch ~/.parallel/will-cite touch ~/.parallel/will-cite
make stopvm make stopvm
mem: ../src/parallel tests-to-run/*mem* wanted-results/*mem* prereqlocal mem: ../src/parallel tests-to-run/*mem* wanted-results/*mem* prereqlocal
time sh Start.sh mem NONE || true time bash Start.sh mem NONE || true
touch ~/.parallel/will-cite touch ~/.parallel/will-cite
make stopvm make stopvm
testdb: ../src/parallel tests-to-run/*sql* wanted-results/*sql* prereqdb testdb: ../src/parallel tests-to-run/*sql* wanted-results/*sql* prereqdb
time sh Start.sh sql NONE time bash Start.sh sql NONE
local: testlocal local: testlocal
true true
testlocal: ../src/parallel tests-to-run/*local* wanted-results/*local* prereqlocal installparallel testlocal: ../src/parallel tests-to-run/*local* wanted-results/*local* prereqlocal installparallel
time sh Start.sh local NONE time bash Start.sh local NONE
prereqlocal: installparallel prereqlocal: installparallel
tcsh -c echo tcsh installed || (echo tcsh is required for testsuite; /bin/false) tcsh -c echo tcsh installed || (echo tcsh is required for testsuite; /bin/false)
@ -40,7 +40,7 @@ prereqlocal: installparallel
which timeout || (echo timeout is required for testsuite; /bin/false) which timeout || (echo timeout is required for testsuite; /bin/false)
prereqremote: installparallel startvm prereqremote: installparallel startvm
parallel --tag ssh parallel@parallel-server{} parallel --minversion 20121021 ::: 1 2 3 || (echo parallel on remote required for testsuite; /bin/true) parallel --timeout 5 --tag ssh parallel@parallel-server{} parallel --minversion 20121021 ::: 1 2 3 || (echo parallel on remote required for testsuite; /bin/true)
startvm: startvm:
# Make sure we can reach the virtual machines # Make sure we can reach the virtual machines

View file

@ -1,4 +1,4 @@
#!/bin/bash #!/bin/bash -x
# Argument can be substring of tests (such as 'local') # Argument can be substring of tests (such as 'local')
@ -7,26 +7,37 @@ SHFILE=/tmp/unittest-parallel.sh
MAX_SEC_PER_TEST=900 MAX_SEC_PER_TEST=900
export TIMEOUT=$MAX_SEC_PER_TEST export TIMEOUT=$MAX_SEC_PER_TEST
if [ "$TRIES" = "3" ] ; then run_test() {
# Try a failing test thrice script="$1"
echo Retrying 3 times base=`basename "$script" .sh`
ls -t tests-to-run/*${1}*.sh | if [ "$TRIES" = "3" ] ; then
grep -v ${2} | # Try 3 times
perl -pe 's:(.*/(.*)).sh:bash $1.sh > actual-results/$2; diff -Naur wanted-results/$2 actual-results/$2 >/dev/null || bash $1.sh > actual-results/$2; diff -Naur wanted-results/$2 actual-results/$2 >/dev/null || bash $1.sh > actual-results/$2; diff -Naur wanted-results/$2 actual-results/$2 || touch $1.sh: ' \ bash $script > actual-results/$base
>$SHFILE diff -Naur wanted-results/$base actual-results/$base >/dev/null ||
else bash $script > actual-results/$base
# Run a failing test once diff -Naur wanted-results/$base actual-results/$base >/dev/null ||
echo Not retrying bash $script > actual-results/$base
ls -t tests-to-run/*${1}*.sh | diff -Naur wanted-results/$base actual-results/$base ||
grep -v ${2} | (touch $script && echo touch $script)
perl -pe 's:(.*/(.*)).sh:bash $1.sh > actual-results/$2; diff -Naur wanted-results/$2 actual-results/$2 || touch $1.sh:' \ else
>$SHFILE # Run only once
fi bash $script > actual-results/$base
diff -Naur wanted-results/$base actual-results/$base ||
(touch $script && echo touch $script)
fi
# Check if it was cleaned up
find /tmp -maxdepth 1 |
perl -ne '/\.(tmb|chr|tms|par)$/ and ++$a and print "TMP NOT CLEAN. FOUND: $_".`touch '$script'`;'
# May be owned by other users
sudo rm -f /tmp/*.{tmb,chr,tms,par}
}
export -f run_test
date date
mkdir -p actual-results mkdir -p actual-results
stdout sh -x $SHFILE | tee testsuite.log ls -t tests-to-run/*${1}*.sh | grep -v ${2} |
rm $SHFILE stdout parallel --tty -tj1 run_test | tee testsuite.log
# If testsuite.log contains @@ then there is a diff # If testsuite.log contains @@ then there is a diff
if grep -q '@@' testsuite.log ; then if grep -q '@@' testsuite.log ; then
false false

View file

@ -94,3 +94,6 @@ echo '### TMUX not found'
TMUX=not-existing parallel --tmux echo ::: 1 TMUX=not-existing parallel --tmux echo ::: 1
EOF EOF
echo '### 1 .par file from --files expected'
ls /tmp/par*.par /var/tmp/par*.par /tmp/*.tms /tmp/*.tmx 2>/dev/null | wc -l
find /tmp/par*.par /var/tmp/par*.par /tmp/*.tms /tmp/*.tmx -mmin -10 2>/dev/null | parallel rm

View file

@ -3,7 +3,7 @@
# Simple jobs that never fails # Simple jobs that never fails
# Each should be taking 3-10s and be possible to run in parallel # Each should be taking 3-10s and be possible to run in parallel
# I.e.: No race conditions, no logins # I.e.: No race conditions, no logins
cat <<'EOF' | sed -e 's/;$/; /;s/$SERVER1/'$SERVER1'/;s/$SERVER2/'$SERVER2'/' | stdout parallel -vj4 -k --joblog /tmp/jl-`basename $0` -L1 cat <<'EOF' | sed -e 's/;$/; /;s/$SERVER1/'$SERVER1'/;s/$SERVER2/'$SERVER2'/' | stdout parallel -vj0 -k --joblog /tmp/jl-`basename $0` -L1
echo '### bug #42089: --results with arg > 256 chars (should be 1 char shorter)' echo '### bug #42089: --results with arg > 256 chars (should be 1 char shorter)'
parallel --results parallel_test_dir echo ::: 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456; parallel --results parallel_test_dir echo ::: 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456;
ls parallel_test_dir/1/ ls parallel_test_dir/1/
@ -72,4 +72,31 @@ echo '**'
echo '### Test slow arguments generation - https://savannah.gnu.org/bugs/?32834'; echo '### Test slow arguments generation - https://savannah.gnu.org/bugs/?32834';
seq 1 3 | parallel -j1 "sleep 2; echo {}" | parallel -kj2 echo seq 1 3 | parallel -j1 "sleep 2; echo {}" | parallel -kj2 echo
echo '**'
echo '### Are children killed if GNU Parallel receives TERM twice? There should be no sleep at the end'
parallel -q bash -c 'sleep 120 & pid=$!; wait $pid' ::: 1 &
T=$!;
sleep 1;
pstree $$;
kill -TERM $T;
sleep 1;
pstree $$;
kill -TERM $T;
sleep 1;
pstree $$;
echo '**'
echo '### Are children killed if GNU Parallel receives INT twice? There should be no sleep at the end'
parallel -q bash -c 'sleep 120 & pid=$!; wait $pid' ::: 1 &
T=$!;
sleep 1;
pstree $$;
kill -INT $T;
sleep 1;
pstree $$;
EOF EOF

View file

@ -2,7 +2,7 @@
# SSH only allowed to localhost/lo # SSH only allowed to localhost/lo
# --retries if ssh dies # --retries if ssh dies
cat <<'EOF' | sed -e s/\$SERVER1/$SERVER1/\;s/\$SERVER2/$SERVER2/ | parallel -vj7 --retries 2 -k --joblog /tmp/jl-`basename $0` -L1 cat <<'EOF' | sed -e s/\$SERVER1/$SERVER1/\;s/\$SERVER2/$SERVER2/ | parallel -vj1 --retries 2 -k --joblog /tmp/jl-`basename $0` -L1
echo '### zsh' echo '### zsh'
ssh zsh@lo 'fun="() { echo function from zsh to zsh \$*; }"; ssh zsh@lo 'fun="() { echo function from zsh to zsh \$*; }";
export fun; export fun;

View file

@ -9,8 +9,8 @@ par_tmux() {
(stdout parallel --timeout 3 --tmux --delay .3 echo '{}{=$_="\\"x$_=}'; echo $?) | par_tmux_filter (stdout parallel --timeout 3 --tmux --delay .3 echo '{}{=$_="\\"x$_=}'; echo $?) | par_tmux_filter
} }
export -f par_tmux export -f par_tmux
#j8
cat <<'EOF' | sed -e 's/;$/; /;s/$SERVER1/'$SERVER1'/;s/$SERVER2/'$SERVER2'/' | stdout parallel -vj8 --retries 2 -k --joblog /tmp/jl-`basename $0` -L1 cat <<'EOF' | sed -e 's/;$/; /;s/$SERVER1/'$SERVER1'/;s/$SERVER2/'$SERVER2'/' | stdout parallel -vj0 --retries 2 -k --joblog /tmp/jl-`basename $0` -L1
echo '### tmux1.9' echo '### tmux1.9'
seq 000 100 | TMUX=tmux1.9 par_tmux seq 000 100 | TMUX=tmux1.9 par_tmux
seq 100 200 | TMUX=tmux1.9 par_tmux seq 100 200 | TMUX=tmux1.9 par_tmux
@ -67,3 +67,5 @@ echo '### Test critical lengths. Must not block'
seq 280 425 | TMUX=tmux1.9 stdout parallel --tmux echo '{}{=$_="a"x$_=}' | par_tmux_filter seq 280 425 | TMUX=tmux1.9 stdout parallel --tmux echo '{}{=$_="a"x$_=}' | par_tmux_filter
EOF EOF
rm -f /tmp/paralocal7*;

View file

@ -1,7 +1,8 @@
#!/bin/bash #!/bin/bash
cd testsuite 2>/dev/null cd testsuite 2>/dev/null
mkdir -p tmp rm -rf tmp
mkdir tmp
cd tmp cd tmp
echo '### test parallel_tutorial' echo '### test parallel_tutorial'
rm -f /tmp/runs rm -f /tmp/runs
@ -14,8 +15,10 @@ perl -ne '$/="\n\n"; /^Output/../^[^O]\S/ and next; /^ / and print;' ../../src/
s/zenity/zenity --timeout=12/; s/zenity/zenity --timeout=12/;
s:/usr/bin/time:/usr/bin/time -f %e:; s:/usr/bin/time:/usr/bin/time -f %e:;
s:ignored_vars:ignored_vars|sort:; s:ignored_vars:ignored_vars|sort:;
# When parallelized: Sleep to make sure the abc-files are made
/%head1/ and $_.="sleep .3\n\n"x10;
' | ' |
stdout bash -x | stdout parallel -j7 -vd'\n\n' |
perl -pe '$|=1; perl -pe '$|=1;
# --files and --tmux # --files and --tmux
s:/tmp/par......(...):/tmp/parXXXXX.$1:; s:/tmp/par......(...):/tmp/parXXXXX.$1:;
@ -42,4 +45,6 @@ perl -ne '$/="\n\n"; /^Output/../^[^O]\S/ and next; /^ / and print;' ../../src/
# + command_X | (Bash outputs these in random order) # + command_X | (Bash outputs these in random order)
s/.*command_[ABC].*\n//; s/.*command_[ABC].*\n//;
' '
# parallel -d'\n\n' # 3+3 .par files (from --files), 1 .tms-file from tmux attach
ls /tmp/par*.par /var/tmp/par*.par /tmp/*.tms /tmp/*.tmx 2>/dev/null | wc -l
find /tmp/par*.par /var/tmp/par*.par /tmp/*.tms /tmp/*.tmx -mmin -10 2>/dev/null | parallel rm

View file

@ -161,3 +161,5 @@ echo '### TMUX not found'
### TMUX not found ### TMUX not found
TMUX=not-existing parallel --tmux echo ::: 1 TMUX=not-existing parallel --tmux echo ::: 1
parallel: Error: not-existing not found in $PATH. parallel: Error: not-existing not found in $PATH.
### 1 .par file from --files expected
1

View file

@ -142,3 +142,23 @@ echo '### Test slow arguments generation - https://savannah.gnu.org/bugs/?32834'
1 1
2 2
3 3
echo '**'
**
echo '### Are children killed if GNU Parallel receives TERM twice? There should be no sleep at the end'
### Are children killed if GNU Parallel receives TERM twice? There should be no sleep at the end
parallel -q bash -c 'sleep 120 & pid=$!; wait $pid' ::: 1 & T=$!; sleep 1; pstree $$; kill -TERM $T; sleep 1; pstree $$; kill -TERM $T; sleep 1; pstree $$; echo '**'
bash-+-perl---bash---sleep
`-pstree
bash-+-perl---bash---sleep
`-pstree
bash---pstree
**
parallel: SIGTERM received. No new jobs will be started.
parallel: Waiting for these 1 jobs to finish. Send SIGTERM again to stop now.
parallel: bash -c sleep\ 120\ \&\ pid=\$\!\;\ wait\ \$pid 1
echo '### Are children killed if GNU Parallel receives INT twice? There should be no sleep at the end'
### Are children killed if GNU Parallel receives INT twice? There should be no sleep at the end
parallel -q bash -c 'sleep 120 & pid=$!; wait $pid' ::: 1 & T=$!; sleep 1; pstree $$; kill -INT $T; sleep 1; pstree $$;
bash-+-perl---bash---sleep
`-pstree
bash---pstree

File diff suppressed because it is too large Load diff