8 foreach - Run an OS or shell command on each input line, similar to xargs(1)
12 foreach [I<OPTIONS>] I<COMMAND> [I<ARGS> ...]
16 Take each input line from stdin as B<DATA>, and run I<COMMAND> with B<DATA> appended to the end of I<ARGS> as a single argument.
18 If C<{}> is there in I<ARGS> then it's substituted with B<DATA> rather than append to the end,
19 unless I<--no-placeholder> is given, because then C<{}> is read literally.
20 Additionally, parse B<DATA> into fields and add each of them to the end of I<ARGS> if I<--fields> is given.
21 Numbered placeholders, like C<{0}>, C<{1}>, ... are substituted with the respective field's value.
23 So if you have not specified any I<ARGS> in the command line and type both I<--data> and I<--fields>,
24 then B<DATA> goes into argv[1], and the first field goes into argv[2], second to argv[3] and so on.
25 If have not given I<--data> nor I<--fields>, then I<--data> is implied.
27 If called with I<--sh> option, I<COMMAND> is run within a shell context;
28 input line goes to C<$DATA>, individual fields go to C<${FIELD[@]}> (0-indexed).
30 Both in command and shell (--sh) modes, individual fields are available in
31 C<$F0>, C<$F1>, ... environment variables.
33 Set I<-d> B<DELIM> if you want to split B<DATA> not by C<$IFS> but by other delimiter chars,
34 eg. C<-d ",:"> for command and colon.
35 There is also C<-t>/C<--tab> option to set delimiter to TAB for your convenience.
43 I<COMMAND> is a shell script and for each B<DATA>, it runs in the same shell context,
44 so variables are preserved across invocations.
48 Pass B<DATA> in the arguments after the user-specified I<ARGS>.
52 Pass individual fields of B<DATA> in the arguments after B<DATA> if I<--data> is given,
53 or after the user-specified I<ARGS> if I<--data> is not given.
55 =item -d, --delimiter DELIM
57 Cut up B<DATA> into fields at B<DELIM> chars.
62 Cut up B<DATA> into fields at TAB chars..
64 =item -P, --no-placeholder
66 Do not substitute C<{}> with B<DATA>.
68 =item -p, --prefix I<TEMPLATE>
70 Print something before each command execution.
71 I<TEMPLATE> is a bash-interpolated string,
72 may contain C<$DATA> and C<${FIELD[n]}>.
73 Probably need to put in single quotes when passing to foreach(1) in the invoking shell.
74 Beware using backtick, command substitution, double quotes, semicolon - since it's B<eval>ed.
82 Stop executing if a I<COMMAND> returns non-zero.
83 Rather exit with the said command's exit status code.
89 ls -l --time-style +%FT%T%z | foreach --data --fields sh -c 'echo size: $5, file: $7'
90 ls -l --time-style +%FT%T%z | foreach --sh 'echo size: ${FIELD[4]}, file: ${FIELD[6]}'
94 Placeholders for field values (C<{0}>, C<{1}>, ...) are considered from 0 up to 99.
95 There must be a limit somewhere, otherwise I had to write a more complex replace routine.
99 Placeholder C<{}> is substituted in all I<ARGS> anywhere, not just stand-alone C<{}> arguments,
101 So be careful using it in shell command arguments like C<sh -e 'echo "data is: {}"'>.
105 xargs(1), xe(1), apply(1), xapply(1) L<https://www.databits.net/~ksb/msrc/local/bin/xapply/xapply.html>
112 # syntax highlighter correction: '
119 FOREACH_PASS_DATA
=maybe
120 FOREACH_PASS_FIELDS
=no
121 FOREACH_REPLACE_PLACEHOLDER
=yes
124 FOREACH_PREFIX_TEMPLATE
=''
138 FOREACH_PASS_DATA
=yes
141 FOREACH_PASS_FIELDS
=yes
151 FOREACH_REPLACE_PLACEHOLDER
=no
155 FOREACH_PREFIX_TEMPLATE
=$1
170 echo "foreach: unknown option: $1" >&2
184 if [ $FOREACH_PASS_FIELDS = yes -a $FOREACH_PASS_DATA != yes ]
188 if [ $FOREACH_PASS_FIELDS != yes ]
190 FOREACH_PASS_DATA
=yes
193 declare -a FOREACH_COMMAND
=("$@")
199 IFS
=${FOREACH_DELIM+$FOREACH_DELIM}${FOREACH_DELIM-$IFS} read -r -a FIELD
<<< "$DATA"
201 for ((idx
= 0; idx
< ${#FIELD[@]}; idx
++))
203 export F
$idx="${FIELD[$idx]}"
205 # unset old F$idx variables
206 while declare -p F
$idx >/dev
/null
2>/dev
/null
212 FOREACH_COMMAND_STATUS
=0
214 declare -a cmd_and_args
=()
216 if [ "$FOREACH_REPLACE_PLACEHOLDER" = yes ]
218 for arg
in "${FOREACH_COMMAND[@]}"
220 if [[ $arg =~ \
{\
} ]]
223 arg
=${arg//$placeholder/$DATA}
224 placeholder_found
=yes
226 for ((idx
= 0; idx
< 100; idx
++))
228 if [[ $arg =~ \
{$idx\
} ]]
231 arg
=${arg//$placeholder/${FIELD[$idx]}}
232 placeholder_found
=yes
235 cmd_and_args
+=("$arg")
238 cmd_and_args
=("${FOREACH_COMMAND[@]}")
241 if [ "$FOREACH_MODE" = command ]
243 if [ "$FOREACH_REPLACE_PLACEHOLDER" != yes -o $placeholder_found = no
]
245 if [ "$FOREACH_PASS_DATA" = yes ]; then cmd_and_args
+=("$DATA"); fi
246 if [ "$FOREACH_PASS_FIELDS" = yes ]; then cmd_and_args
+=("${FIELD[@]}"); fi
250 if [ "$FOREACH_VERBOSE" = yes ]
252 echo "foreach: ${cmd_and_args[*]}" >&2
255 eval "FOREACH_PREFIX=\"$FOREACH_PREFIX_TEMPLATE\""
256 echo -n "$FOREACH_PREFIX"
258 if [ "$FOREACH_DRYRUN" = no
]
260 if [ "$FOREACH_MODE" = command ]
262 command "${cmd_and_args[@]}"
263 FOREACH_COMMAND_STATUS
=$?
265 eval "${cmd_and_args[@]}"
266 FOREACH_COMMAND_STATUS
=$?
270 if [ $FOREACH_ERREXIT = yes -a $FOREACH_COMMAND_STATUS != 0 ]