diff --git a/NEWS b/NEWS index 5804033d..01061d14 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,28 @@ +20230522 + +New in this release: + +* Bug fixes and man page updates. + +News about GNU Parallel: + +* Introduction to parallel computing https://youtu.be/fvrHXV8yqU4?t=2874 + +* Script for summarizing GNU parallel joblog file https://github.com/gavinmdouglas/parallel_joblog_summary + +* How to copy a single file to multiple directories in Linux or Unix https://www.cyberciti.biz/faq/linux-unix-copy-a-file-to-multiple-directories-using-cp-command/ + +* Search in your Jupyter notebooks from the CLI, fast. https://dev.to/attilavm/search-in-your-jupyter-notebooks-from-the-cli-fast-1408 + +* Parallel Job Orchestration with GNU Parallel https://www.youtube.com/watch?v=2tVpUfND3LI + +* Optional Individual Submission 4 Job Handling 20266001 - GNU Parallel https://www.youtube.com/watch?v=eC_RPuFCcU8 + +* DOE Cross-facility Workflows Training - April 12, 2023 https://www.youtube.com/watch?v=Ke3sirM-aQQ + +* How to run MiXCR 4.x on multiple patient samples using GNU Parallel https://www.youtube.com/watch?v=OXg-WHlB_dk + + 20230422 New in this release: diff --git a/README b/README index 591d41fe..6b8dcfee 100644 --- a/README +++ b/README @@ -57,11 +57,11 @@ document. Full installation of GNU Parallel is as simple as: - wget https://ftpmirror.gnu.org/parallel/parallel-20230422.tar.bz2 - wget https://ftpmirror.gnu.org/parallel/parallel-20230422.tar.bz2.sig - gpg parallel-20230422.tar.bz2.sig - bzip2 -dc parallel-20230422.tar.bz2 | tar xvf - - cd parallel-20230422 + wget https://ftpmirror.gnu.org/parallel/parallel-20230522.tar.bz2 + wget https://ftpmirror.gnu.org/parallel/parallel-20230522.tar.bz2.sig + gpg parallel-20230522.tar.bz2.sig + bzip2 -dc parallel-20230522.tar.bz2 | tar xvf - + cd parallel-20230522 ./configure && make && sudo make install @@ -70,11 +70,11 @@ Full installation of GNU Parallel is as simple as: If you are not root you can add ~/bin to your path and install in ~/bin and ~/share: - wget https://ftpmirror.gnu.org/parallel/parallel-20230422.tar.bz2 - wget https://ftpmirror.gnu.org/parallel/parallel-20230422.tar.bz2.sig - gpg parallel-20230422.tar.bz2.sig - bzip2 -dc parallel-20230422.tar.bz2 | tar xvf - - cd parallel-20230422 + wget https://ftpmirror.gnu.org/parallel/parallel-20230522.tar.bz2 + wget https://ftpmirror.gnu.org/parallel/parallel-20230522.tar.bz2.sig + gpg parallel-20230522.tar.bz2.sig + bzip2 -dc parallel-20230522.tar.bz2 | tar xvf - + cd parallel-20230522 ./configure --prefix=$HOME && make && make install Or if your system lacks 'make' you can simply copy src/parallel @@ -122,8 +122,8 @@ will love you for it. When using programs that use GNU Parallel to process data for publication please cite: - Tange, O. (2023, April 22). GNU Parallel 20230422 ('Grand Jury'). - Zenodo. https://doi.org/10.5281/zenodo.7855617 + Tange, O. (2023, May 22). GNU Parallel 20230522 ('Charles'). + Zenodo. https://doi.org/10.5281/zenodo.7958356 Copyright (C) 2007-2022 Ole Tange, http://ole.tange.dk and Free Software Foundation, Inc. diff --git a/configure b/configure index 59aef3ff..6b815d2c 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for parallel 20230422. +# Generated by GNU Autoconf 2.71 for parallel 20230522. # # Report bugs to . # @@ -610,8 +610,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='parallel' PACKAGE_TARNAME='parallel' -PACKAGE_VERSION='20230422' -PACKAGE_STRING='parallel 20230422' +PACKAGE_VERSION='20230522' +PACKAGE_STRING='parallel 20230522' PACKAGE_BUGREPORT='bug-parallel@gnu.org' PACKAGE_URL='' @@ -1246,7 +1246,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures parallel 20230422 to adapt to many kinds of systems. +\`configure' configures parallel 20230522 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1313,7 +1313,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of parallel 20230422:";; + short | recursive ) echo "Configuration of parallel 20230522:";; esac cat <<\_ACEOF @@ -1390,7 +1390,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -parallel configure 20230422 +parallel configure 20230522 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. @@ -1427,7 +1427,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by parallel $as_me 20230422, which was +It was created by parallel $as_me 20230522, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -2379,7 +2379,7 @@ fi # Define the identity of the package. PACKAGE='parallel' - VERSION='20230422' + VERSION='20230522' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h @@ -3026,7 +3026,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by parallel $as_me 20230422, which was +This file was extended by parallel $as_me 20230522, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -3090,7 +3090,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -parallel config.status 20230422 +parallel config.status 20230522 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index d311c389..f84faf52 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([parallel],[20230422],[bug-parallel@gnu.org]) +AC_INIT([parallel],[20230522],[bug-parallel@gnu.org]) AM_INIT_AUTOMAKE([-Wall -Werror foreign]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([ diff --git a/doc/haikus b/doc/haikus index aa01e539..145df923 100644 --- a/doc/haikus +++ b/doc/haikus @@ -5,9 +5,6 @@ Quote of the month: - Recently learned how to use GNU parallel (from ChatGPT, no less!) and I've gone mad with power - -- Mark, Anthropomorphic Anuran @reject_resubmit@twitter - gnu parallel is actually like. really easy -- tom (era) @slimefiend@twitter @@ -231,6 +228,9 @@ https://negfeedback.blogspot.com/2020/05/indispensable-command-line-tools.html === Used === + Recently learned how to use GNU parallel (from ChatGPT, no less!) and I've gone mad with power + -- Mark, Anthropomorphic Anuran @reject_resubmit@twitter + parallel might be one of the best utilities out there -- @ThePrimeagen ThePrimeagen diff --git a/doc/release_new_version b/doc/release_new_version index d313f052..8492bc78 100644 --- a/doc/release_new_version +++ b/doc/release_new_version @@ -263,17 +263,18 @@ from:tange@gnu.org to:parallel@gnu.org, bug-parallel@gnu.org stable-bcc: Jesse Alama -Subject: GNU Parallel 20230522 ('Charles<<>>') released +Subject: GNU Parallel 20230522 ('Charles') released [stable] -GNU Parallel 20230522 ('<<>>') has been released. It is available for download at: lbry://@GnuParallel:4 +GNU Parallel 20230522 ('Charles') has been released. It is available for download at: lbry://@GnuParallel:4 Quote of the month: - <<>> + Recently learned how to use GNU parallel (from ChatGPT, no less!) and I've gone mad with power + -- Mark, Anthropomorphic Anuran @reject_resubmit@twitter New in this release: -<<>> +* No new features. This is a candidate for a stable release. * Bug fixes and man page updates. @@ -283,19 +284,17 @@ News about GNU Parallel: * Script for summarizing GNU parallel joblog file https://github.com/gavinmdouglas/parallel_joblog_summary -https://www.cyberciti.biz/faq/linux-unix-copy-a-file-to-multiple-directories-using-cp-command/ -https://dev.to/attilavm/search-in-your-jupyter-notebooks-from-the-cli-fast-1408 +* How to copy a single file to multiple directories in Linux or Unix https://www.cyberciti.biz/faq/linux-unix-copy-a-file-to-multiple-directories-using-cp-command/ -https://www.youtube.com/watch?v=2tVpUfND3LI -https://www.youtube.com/watch?v=eC_RPuFCcU8 -https://www.youtube.com/watch?v=3vFxDqgcdVI +* Search in your Jupyter notebooks from the CLI, fast. https://dev.to/attilavm/search-in-your-jupyter-notebooks-from-the-cli-fast-1408 -https://www.youtube.com/watch?v=Ke3sirM-aQQ -https://www.youtube.com/watch?v=OXg-WHlB_dk +* Parallel Job Orchestration with GNU Parallel https://www.youtube.com/watch?v=2tVpUfND3LI - - -<<>> +* Optional Individual Submission 4 Job Handling 20266001 - GNU Parallel https://www.youtube.com/watch?v=eC_RPuFCcU8 + +* DOE Cross-facility Workflows Training - April 12, 2023 https://www.youtube.com/watch?v=Ke3sirM-aQQ + +* How to run MiXCR 4.x on multiple patient samples using GNU Parallel https://www.youtube.com/watch?v=OXg-WHlB_dk GNU Parallel - For people who live life in the parallel lane. diff --git a/packager/obs/home-tange/parallel/.osc/_files b/packager/obs/home-tange/parallel/.osc/_files index e49f15a7..a7de71c0 100644 --- a/packager/obs/home-tange/parallel/.osc/_files +++ b/packager/obs/home-tange/parallel/.osc/_files @@ -1,7 +1,7 @@ - - - - - - + + + + + + diff --git a/packager/obs/home-tange/parallel/.osc/parallel.spec b/packager/obs/home-tange/parallel/.osc/parallel.spec index 0833d121..dd955daa 100644 --- a/packager/obs/home-tange/parallel/.osc/parallel.spec +++ b/packager/obs/home-tange/parallel/.osc/parallel.spec @@ -1,7 +1,7 @@ Summary: Shell tool for executing jobs in parallel Name: parallel -Version: 20230422 +Version: 20230522 Release: 2.1 License: GPL-3.0-or-later Group: Productivity/File utilities diff --git a/src/env_parallel.ash b/src/env_parallel.ash index 0b0393e9..605af275 100755 --- a/src/env_parallel.ash +++ b/src/env_parallel.ash @@ -393,7 +393,7 @@ _parset_main() { fi if [ "$_parset_NAME" = "--version" ] ; then # shellcheck disable=SC2006 - echo "parset 20230423 (GNU parallel `parallel --minversion 1`)" + echo "parset 20230522 (GNU parallel `parallel --minversion 1`)" echo "Copyright (C) 2007-2023 Ole Tange, http://ole.tange.dk and Free Software" echo "Foundation, Inc." echo "License GPLv3+: GNU GPL version 3 or later " diff --git a/src/env_parallel.bash b/src/env_parallel.bash index 24c51611..d55512bf 100755 --- a/src/env_parallel.bash +++ b/src/env_parallel.bash @@ -395,7 +395,7 @@ _parset_main() { fi if [ "$_parset_NAME" = "--version" ] ; then # shellcheck disable=SC2006 - echo "parset 20230423 (GNU parallel `parallel --minversion 1`)" + echo "parset 20230522 (GNU parallel `parallel --minversion 1`)" echo "Copyright (C) 2007-2023 Ole Tange, http://ole.tange.dk and Free Software" echo "Foundation, Inc." echo "License GPLv3+: GNU GPL version 3 or later " diff --git a/src/env_parallel.dash b/src/env_parallel.dash index d9133e06..13df896b 100755 --- a/src/env_parallel.dash +++ b/src/env_parallel.dash @@ -393,7 +393,7 @@ _parset_main() { fi if [ "$_parset_NAME" = "--version" ] ; then # shellcheck disable=SC2006 - echo "parset 20230423 (GNU parallel `parallel --minversion 1`)" + echo "parset 20230522 (GNU parallel `parallel --minversion 1`)" echo "Copyright (C) 2007-2023 Ole Tange, http://ole.tange.dk and Free Software" echo "Foundation, Inc." echo "License GPLv3+: GNU GPL version 3 or later " diff --git a/src/env_parallel.ksh b/src/env_parallel.ksh index baadd7c3..b6744571 100755 --- a/src/env_parallel.ksh +++ b/src/env_parallel.ksh @@ -376,7 +376,7 @@ _parset_main() { fi if [ "$_parset_NAME" = "--version" ] ; then # shellcheck disable=SC2006 - echo "parset 20230423 (GNU parallel `parallel --minversion 1`)" + echo "parset 20230522 (GNU parallel `parallel --minversion 1`)" echo "Copyright (C) 2007-2023 Ole Tange, http://ole.tange.dk and Free Software" echo "Foundation, Inc." echo "License GPLv3+: GNU GPL version 3 or later " diff --git a/src/env_parallel.mksh b/src/env_parallel.mksh index ddecb6f2..e0b9b166 100644 --- a/src/env_parallel.mksh +++ b/src/env_parallel.mksh @@ -378,7 +378,7 @@ _parset_main() { fi if [ "$_parset_NAME" = "--version" ] ; then # shellcheck disable=SC2006 - echo "parset 20230423 (GNU parallel `parallel --minversion 1`)" + echo "parset 20230522 (GNU parallel `parallel --minversion 1`)" echo "Copyright (C) 2007-2023 Ole Tange, http://ole.tange.dk and Free Software" echo "Foundation, Inc." echo "License GPLv3+: GNU GPL version 3 or later " diff --git a/src/env_parallel.sh b/src/env_parallel.sh index c35613d6..5a8ada0d 100755 --- a/src/env_parallel.sh +++ b/src/env_parallel.sh @@ -393,7 +393,7 @@ _parset_main() { fi if [ "$_parset_NAME" = "--version" ] ; then # shellcheck disable=SC2006 - echo "parset 20230423 (GNU parallel `parallel --minversion 1`)" + echo "parset 20230522 (GNU parallel `parallel --minversion 1`)" echo "Copyright (C) 2007-2023 Ole Tange, http://ole.tange.dk and Free Software" echo "Foundation, Inc." echo "License GPLv3+: GNU GPL version 3 or later " diff --git a/src/env_parallel.zsh b/src/env_parallel.zsh index 759391ea..69a6a769 100755 --- a/src/env_parallel.zsh +++ b/src/env_parallel.zsh @@ -368,7 +368,7 @@ _parset_main() { fi if [ "$_parset_NAME" = "--version" ] ; then # shellcheck disable=SC2006 - echo "parset 20230423 (GNU parallel `parallel --minversion 1`)" + echo "parset 20230522 (GNU parallel `parallel --minversion 1`)" echo "Copyright (C) 2007-2023 Ole Tange, http://ole.tange.dk and Free Software" echo "Foundation, Inc." echo "License GPLv3+: GNU GPL version 3 or later " diff --git a/src/niceload b/src/niceload index 6836b56c..fc3626a2 100755 --- a/src/niceload +++ b/src/niceload @@ -26,7 +26,7 @@ use strict; use Getopt::Long; $Global::progname="niceload"; -$Global::version = 20230423; +$Global::version = 20230522; Getopt::Long::Configure("bundling","require_order"); get_options_from_array(\@ARGV) || die_usage(); if($opt::version) { diff --git a/src/parallel b/src/parallel index a577a330..782dc86a 100755 --- a/src/parallel +++ b/src/parallel @@ -2799,7 +2799,7 @@ sub check_invalid_option_combinations() { sub init_globals() { # Defaults: - $Global::version = 20230423; + $Global::version = 20230522; $Global::progname = 'parallel'; $::name = "GNU Parallel"; $Global::infinity = 2**31; @@ -5748,8 +5748,8 @@ sub usage() { "If you use programs that use GNU Parallel to process data for an article in a", "scientific publication, please cite:", "", - " Tange, O. (2023, April 22). GNU Parallel 20230422 ('Grand Jury').", - " Zenodo. https://doi.org/10.5281/zenodo.7855617", + " Tange, O. (2023, May 22). GNU Parallel 20230522 ('Charles').", + " Zenodo. https://doi.org/10.5281/zenodo.7958356", "", # Before changing these lines, please read # https://www.gnu.org/software/parallel/parallel_design.html#citation-notice @@ -5781,8 +5781,8 @@ sub citation_notice() { "If you use programs that use GNU Parallel to process data for an article in a", "scientific publication, please cite:", "", - " Tange, O. (2023, April 22). GNU Parallel 20230422 ('Grand Jury').", - " Zenodo. https://doi.org/10.5281/zenodo.7855617", + " Tange, O. (2023, May 22). GNU Parallel 20230522 ('Charles').", + " Zenodo. https://doi.org/10.5281/zenodo.7958356", "", # Before changing these line, please read # https://www.gnu.org/software/parallel/parallel_design.html#citation-notice and @@ -5909,20 +5909,20 @@ sub citation() { "If you use programs that use GNU Parallel to process data for an article in a", "scientific publication, please cite:", "", - "\@software{tange_2023_7855617,", + "\@software{tange_2023_7958356,", " author = {Tange, Ole},", - " title = {GNU Parallel 20230422 ('Grand Jury')},", - " month = Apr,", + " title = {GNU Parallel 20230522 ('Charles')},", + " month = May,", " year = 2023,", " note = {{GNU Parallel is a general parallelizer to run", " multiple serial command line programs in parallel", " without changing them.}},", " publisher = {Zenodo},", - " doi = {10.5281/zenodo.7855617},", - " url = {https://doi.org/10.5281/zenodo.7855617}", + " doi = {10.5281/zenodo.7958356},", + " url = {https://doi.org/10.5281/zenodo.7958356}", "}", "", - "(Feel free to use \\nocite{tange_2023_7855617})", + "(Feel free to use \\nocite{tange_2023_7958356})", "", # Before changing these lines, please read # https://www.gnu.org/software/parallel/parallel_design.html#citation-notice and diff --git a/src/parallel.pod b/src/parallel.pod index 3fe5cb45..35656395 100644 --- a/src/parallel.pod +++ b/src/parallel.pod @@ -1562,9 +1562,9 @@ Similar to B<--memfree>. See also: B<--memfree> B<--load> -=item B<--latest-line> (alpha testing) +=item B<--latest-line> (beta testing) -=item B<--ll> (alpha testing) +=item B<--ll> (beta testing) Print the lastest line. Each job gets a single line that is updated with the lastest output from the job. @@ -1834,13 +1834,13 @@ Similar to B<--tty> but does not set B<--jobs> or B<--ungroup>. See also: B<--tty> -=item B<--output-as-files> (beta testing) +=item B<--output-as-files> -=item B<--outputasfiles> (beta testing) +=item B<--outputasfiles> -=item B<--files> (beta testing) +=item B<--files> -=item B<--files0> (beta testing) +=item B<--files0> Save output to files. @@ -1892,7 +1892,7 @@ See also: B<--block> B<--block-timeout> B<--recstart> B<--recend> B<--fifo> B<--cat> B<--pipe-part> B<-N> B<-L> B<--round-robin> -=item B<--pipe-part> +=item B<--pipe-part> (alpha testing) Pipe parts of a physical file. diff --git a/src/parsort b/src/parsort index 3b1eeb92..73fab1b9 100755 --- a/src/parsort +++ b/src/parsort @@ -137,7 +137,7 @@ GetOptions( "help" => \$opt::dummy, ) || exit(255); $Global::progname = ($0 =~ m:(^|/)([^/]+)$:)[1]; -$Global::version = 20230423; +$Global::version = 20230522; if($opt::version) { version(); exit 0; } # Remove -D and --parallel=N my @s = (grep { ! /^-D$|^--parallel=\S+$/ } diff --git a/src/sql b/src/sql index a0172556..5d59be42 100755 --- a/src/sql +++ b/src/sql @@ -670,7 +670,7 @@ $Global::Initfile && unlink $Global::Initfile; exit ($err); sub parse_options { - $Global::version = 20230423; + $Global::version = 20230522; $Global::progname = 'sql'; # This must be done first as this may exec myself diff --git a/testsuite/input-files/perl-v5.14.2/lib/Text/ParseWords.pm b/testsuite/input-files/perl-v5.14.2/lib/Text/ParseWords.pm new file mode 100644 index 00000000..f1b59379 --- /dev/null +++ b/testsuite/input-files/perl-v5.14.2/lib/Text/ParseWords.pm @@ -0,0 +1,294 @@ +package Text::ParseWords; + +use strict; +require 5.006; +our $VERSION = "3.27"; + + +use Exporter; +our @ISA = qw(Exporter); +our @EXPORT = qw(shellwords quotewords nested_quotewords parse_line); +our @EXPORT_OK = qw(old_shellwords); +our $PERL_SINGLE_QUOTE; + + +sub shellwords { + my (@lines) = @_; + my @allwords; + + foreach my $line (@lines) { + $line =~ s/^\s+//; + my @words = parse_line('\s+', 0, $line); + pop @words if (@words and !defined $words[-1]); + return() unless (@words || !length($line)); + push(@allwords, @words); + } + return(@allwords); +} + + + +sub quotewords { + my($delim, $keep, @lines) = @_; + my($line, @words, @allwords); + + foreach $line (@lines) { + @words = parse_line($delim, $keep, $line); + return() unless (@words || !length($line)); + push(@allwords, @words); + } + return(@allwords); +} + + + +sub nested_quotewords { + my($delim, $keep, @lines) = @_; + my($i, @allwords); + + for ($i = 0; $i < @lines; $i++) { + @{$allwords[$i]} = parse_line($delim, $keep, $lines[$i]); + return() unless (@{$allwords[$i]} || !length($lines[$i])); + } + return(@allwords); +} + + + +sub parse_line { + my($delimiter, $keep, $line) = @_; + my($word, @pieces); + + no warnings 'uninitialized'; # we will be testing undef strings + + while (length($line)) { + # This pattern is optimised to be stack conservative on older perls. + # Do not refactor without being careful and testing it on very long strings. + # See Perl bug #42980 for an example of a stack busting input. + $line =~ s/^ + (?: + # double quoted string + (") # $quote + ((?>[^\\"]*(?:\\.[^\\"]*)*))" # $quoted + | # --OR-- + # singe quoted string + (') # $quote + ((?>[^\\']*(?:\\.[^\\']*)*))' # $quoted + | # --OR-- + # unquoted string + ( # $unquoted + (?:\\.|[^\\"'])*? + ) + # followed by + ( # $delim + \Z(?!\n) # EOL + | # --OR-- + (?-x:$delimiter) # delimiter + | # --OR-- + (?!^)(?=["']) # a quote + ) + )//xs or return; # extended layout + my ($quote, $quoted, $unquoted, $delim) = (($1 ? ($1,$2) : ($3,$4)), $5, $6); + + + return() unless( defined($quote) || length($unquoted) || length($delim)); + + if ($keep) { + $quoted = "$quote$quoted$quote"; + } + else { + $unquoted =~ s/\\(.)/$1/sg; + if (defined $quote) { + $quoted =~ s/\\(.)/$1/sg if ($quote eq '"'); + $quoted =~ s/\\([\\'])/$1/g if ( $PERL_SINGLE_QUOTE && $quote eq "'"); + } + } + $word .= substr($line, 0, 0); # leave results tainted + $word .= defined $quote ? $quoted : $unquoted; + + if (length($delim)) { + push(@pieces, $word); + push(@pieces, $delim) if ($keep eq 'delimiters'); + undef $word; + } + if (!length($line)) { + push(@pieces, $word); + } + } + return(@pieces); +} + + + +sub old_shellwords { + + # Usage: + # use ParseWords; + # @words = old_shellwords($line); + # or + # @words = old_shellwords(@lines); + # or + # @words = old_shellwords(); # defaults to $_ (and clobbers it) + + no warnings 'uninitialized'; # we will be testing undef strings + local *_ = \join('', @_) if @_; + my (@words, $snippet); + + s/\A\s+//; + while ($_ ne '') { + my $field = substr($_, 0, 0); # leave results tainted + for (;;) { + if (s/\A"(([^"\\]|\\.)*)"//s) { + ($snippet = $1) =~ s#\\(.)#$1#sg; + } + elsif (/\A"/) { + require Carp; + Carp::carp("Unmatched double quote: $_"); + return(); + } + elsif (s/\A'(([^'\\]|\\.)*)'//s) { + ($snippet = $1) =~ s#\\(.)#$1#sg; + } + elsif (/\A'/) { + require Carp; + Carp::carp("Unmatched single quote: $_"); + return(); + } + elsif (s/\A\\(.?)//s) { + $snippet = $1; + } + elsif (s/\A([^\s\\'"]+)//) { + $snippet = $1; + } + else { + s/\A\s+//; + last; + } + $field .= $snippet; + } + push(@words, $field); + } + return @words; +} + +1; + +__END__ + +=head1 NAME + +Text::ParseWords - parse text into an array of tokens or array of arrays + +=head1 SYNOPSIS + + use Text::ParseWords; + @lists = nested_quotewords($delim, $keep, @lines); + @words = quotewords($delim, $keep, @lines); + @words = shellwords(@lines); + @words = parse_line($delim, $keep, $line); + @words = old_shellwords(@lines); # DEPRECATED! + +=head1 DESCRIPTION + +The &nested_quotewords() and "ewords() functions accept a delimiter +(which can be a regular expression) +and a list of lines and then breaks those lines up into a list of +words ignoring delimiters that appear inside quotes. "ewords() +returns all of the tokens in a single long list, while &nested_quotewords() +returns a list of token lists corresponding to the elements of @lines. +&parse_line() does tokenizing on a single string. The &*quotewords() +functions simply call &parse_line(), so if you're only splitting +one line you can call &parse_line() directly and save a function +call. + +The $keep argument is a boolean flag. If true, then the tokens are +split on the specified delimiter, but all other characters (quotes, +backslashes, etc.) are kept in the tokens. If $keep is false then the +&*quotewords() functions remove all quotes and backslashes that are +not themselves backslash-escaped or inside of single quotes (i.e., +"ewords() tries to interpret these characters just like the Bourne +shell). NB: these semantics are significantly different from the +original version of this module shipped with Perl 5.000 through 5.004. +As an additional feature, $keep may be the keyword "delimiters" which +causes the functions to preserve the delimiters in each string as +tokens in the token lists, in addition to preserving quote and +backslash characters. + +&shellwords() is written as a special case of "ewords(), and it +does token parsing with whitespace as a delimiter-- similar to most +Unix shells. + +=head1 EXAMPLES + +The sample program: + + use Text::ParseWords; + @words = quotewords('\s+', 0, q{this is "a test" of\ quotewords \"for you}); + $i = 0; + foreach (@words) { + print "$i: <$_>\n"; + $i++; + } + +produces: + + 0: + 1: + 2: + 3: + 4: <"for> + 5: + +demonstrating: + +=over 4 + +=item 0 + +a simple word + +=item 1 + +multiple spaces are skipped because of our $delim + +=item 2 + +use of quotes to include a space in a word + +=item 3 + +use of a backslash to include a space in a word + +=item 4 + +use of a backslash to remove the special meaning of a double-quote + +=item 5 + +another simple word (note the lack of effect of the +backslashed double-quote) + +=back + +Replacing C +with C +is a simpler way to accomplish the same thing. + +=head1 AUTHORS + +Maintainer: Alexandr Ciornii . + +Previous maintainer: Hal Pomeranz , 1994-1997 (Original +author unknown). Much of the code for &parse_line() (including the +primary regexp) from Joerk Behrends . + +Examples section another documentation provided by John Heidemann + + +Bug reports, patches, and nagging provided by lots of folks-- thanks +everybody! Special thanks to Michael Schwern +for assuring me that a &nested_quotewords() would be useful, and to +Jeff Friedl for telling me not to worry about +error-checking (sort of-- you had to be there). + +=cut