1#!/usr/bin/env fish 2 3# This file must be sourced in fish: 4# 5# . (which env_parallel.fish) 6# 7# after which 'env_parallel' works 8# 9# 10# Copyright (C) 2016-2021 Ole Tange, http://ole.tange.dk and Free 11# Software Foundation, Inc. 12# 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. 17# 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. 22# 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 27# 28# SPDX-FileCopyrightText: 2021 Ole Tange, http://ole.tange.dk and Free Software and Foundation, Inc. 29# SPDX-License-Identifier: GPL-3.0-or-later 30 31# If you are a fisherman feel free to improve the code 32# 33# The code needs to deal with variables like: 34# set funky (perl -e 'print pack "c*", 2..254') 35# 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. 45 46function env_parallel 47 # env_parallel.fish 48 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 ) 99 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; 107 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 } 172 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 194end 195