Released as 20240522 ('Tbilisi')
[parallel.git] / src / env_parallel.fish
blobb0abc5001f7cfac41634b8d15234c5fc63d4c0a1
1 #!/usr/bin/env fish
3 # This file must be sourced in fish:
5 #   . (which env_parallel.fish)
7 # after which 'env_parallel' works
10 # Copyright (C) 2016-2024 Ole Tange, http://ole.tange.dk and Free
11 # Software Foundation, Inc.
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 3 of the License, or
16 # (at your option) any later version.
18 # This program is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21 # General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, see <http://www.gnu.org/licenses/>
25 # or write to the Free Software Foundation, Inc., 51 Franklin St,
26 # Fifth Floor, Boston, MA 02110-1301 USA
28 # SPDX-FileCopyrightText: 2021-2024 Ole Tange, http://ole.tange.dk and Free Software and Foundation, Inc.
29 # SPDX-License-Identifier: GPL-3.0-or-later
31 # If you are a fisherman feel free to improve the code
33 # The code needs to deal with variables like:
34 #   set funky (perl -e 'print pack "c*", 2..254')
36 # Problem:
37 #   Tell the difference between:
38 #     set tmp "a'  'b'  'c"
39 #     set tmparr1 "a'  'b"  'c'
40 #     set tmparr2 'a'  "b'  'c"
41 #   The output from `set` is exactly the same.
42 # Solution:
43 #   for-loop for each variable. Each value is separated with a
44 #   separator.
46 function env_parallel
47   # env_parallel.fish
49   # --session
50   perl -e 'exit grep { /^--session/ } @ARGV' -- $argv; or begin;
51     setenv PARALLEL_IGNORED_NAMES (
52         begin;
53           functions -n
54           set -n;
55         end | perl -pe 's/\n/,/g';
56     )
57     return 0
58   end;
59   setenv PARALLEL_ENV (
60     begin;
61       set _grep_REGEXP (
62         begin;
63           perl -e '
64             for(@ARGV){
65                 /^_$/ and $next_is_env = 0;
66                 $next_is_env and push @envvar, split/,/, $_;
67                 $next_is_env = /^--env$/;
68             }
69             $vars = join "|",map { quotemeta $_ } @envvar;
70             print $vars ? "($vars)" : "(.*)";
71             ' -- $argv;
72         end;
73       )
74       # Deal with --env _
75       set _ignore_UNDERSCORE (
76         begin;
77           perl -e '
78             for(@ARGV){
79                 $next_is_env and push @envvar, split/,/, $_;
80                 $next_is_env=/^--env$/;
81             }
82             if(grep { /^_$/ } @envvar) {
83                 if(not open(IN, "<", "$ENV{HOME}/.parallel/ignored_vars")) {
84                     print STDERR "parallel: Error: ",
85                     "Run \"parallel --record-env\" in a clean environment first.\n";
86                 } else {
87                     chomp(@ignored_vars = <IN>);
88                 }
89             }
90             if($ENV{PARALLEL_IGNORED_NAMES}) {
91                 push @ignored_vars, split/,/, $ENV{PARALLEL_IGNORED_NAMES};
92                 chomp @ignored_vars;
93             }
94             $vars = join "|",map { quotemeta $_ } @ignored_vars;
95             print $vars ? "($vars)" : "(,,nO,,VaRs,,)";
96             ' -- $argv;
97         end;
98       )
100       # --record-env
101       perl -e 'exit grep { /^--record-env$/ } @ARGV' -- $argv; or begin;
102         begin;
103           functions -n | perl -pe 's/,/\n/g';
104           set -n;
105         end | cat > $HOME/.parallel/ignored_vars;
106       end;
108       # Export function definitions
109       # Keep the ones from --env
110       # Ignore the ones from ~/.parallel/ignored_vars
111       # Dump each function defition
112       # Replace \001 with \002 because \001 is used by env_parallel
113       # Convert \n to \001
114       functions -n | perl -pe 's/,/\n/g' | \
115         grep -Ev '^(PARALLEL_ENV|PARALLEL_TMP)$' | \
116         grep -E "^$_grep_REGEXP"\$ | grep -vE "^$_ignore_UNDERSCORE"\$ | \
117         while read d; functions $d; end | \
118         perl -pe 's/\001/\002/g and not $printed++ and print STDERR
119           "env_parallel: Warning: ASCII value 1 in functions is not supported\n";
120                   s/\n/\001/g';
121       # Convert scalar vars to fish \XX quoting
122       # Keep the ones from --env
123       # Ignore the ones from ~/.parallel/ignored_vars
124       # Ignore read only vars
125       # Execute 'set' of the content
126       eval (set -L | \
127         grep -Ev '^(PARALLEL_TMP)$' | \
128         grep -E "^$_grep_REGEXP " | grep -vE "^$_ignore_UNDERSCORE " | \
129         perl -ne 'chomp;
130           ($name,$val)=split(/ /,$_,2);
131           $name=~/^(HOME|USER|COLUMNS|FISH_VERSION|LINES|PWD|SHLVL|_|
132                     history|status|version)$|\./x and next;
133           if($val=~/^'"'"'/) { next; }
134           print "set $name \"\$$name\";\n";
135         ')
136       # Generate commands to set scalar variables
137       # Keep the ones from --env
138       # Ignore the ones from ~/.parallel/ignored_vars
139       #
140       begin;
141         for v in (set -n | \
142           grep -Ev '^(PARALLEL_TMP)$' | \
143           grep -E "^$_grep_REGEXP\$" | grep -vE "^$_ignore_UNDERSCORE\$");
144           # Separate variables with the string: \000
145           # array_name1 val1\0
146           # array_name1 val2\0
147           # array_name2 val3\0
148           # array_name2 val4\0
149           eval "for i in \$$v;
150             echo -n $v \$i;
151             perl -e print\\\"\\\\0\\\";
152           end;"
153         end;
154         # A final line to flush the last variable in Perl
155         perl -e print\"\\0\";
156       end | perl -0 -ne '
157         # Remove separator string \0
158         chop;
159         # Split line into name and value
160         ($name,$val)=split(/ /,$_,2);
161         # Ignore read-only vars
162         $name=~/^(HOME|USER|COLUMNS|FISH_VERSION|LINES|PWD|SHLVL|_|
163                   fish_pid|history|hostname|status|version)$/x and next;
164         # Single quote $val
165         if($val =~ /[^-_.+a-z0-9\/]/i) {
166           $val =~ s/\047/\047"\047"\047/g;  # "-quote single quotes
167           $val = "\047$val\047";            # single-quote entire string
168           $val =~ s/^\047\047|\047\047$//g; # Remove unneeded '' at ends
169         } elsif ($val eq "") {
170           $val = "\047\047";
171         }
173         if($name ne $last and $last) {
174           # The $name is different, so this is a new variable.
175           # Print the last one.
176           # Separate list elements by 2 spaces
177           $"="  ";
178           print "set $last @qval;\n";
179           @qval=();
180         }
181         push @qval,$val;
182         $last=$name;
183         '| \
184         perl -pe 's/\001/\002/g and not $printed++ and print STDERR
185           "env_parallel: Warning: ASCII value 1 in variables is not supported\n";
186           s/\n/\001/g'
187     end;
188     )
189   # If --record-env: exit
190   perl -e 'exit grep { /^--record-env$/ } @ARGV' -- $argv; and parallel $argv;
191   set _parallel_exit_CODE $status
192   set -e PARALLEL_ENV
193   return $_parallel_exit_CODE