3

I need to run a command that looks like this

mycli --file test.zip --file another_test.zip

How could I run that dynamically with all zip files in a directory? I'm sure I could pipe the files from a find command, but I don't know how to actually append them as arguments to another command and my bash-fu is not great

Kusalananda
  • 333,661
blitzmann
  • 405

3 Answers3

6

Using an array:

unset -v args
declare -a args
for file in *.zip
do
  args+=( --file "$file" )
done
mycli "${args[@]}"

Or, POSIXly:

set --
for file in *.zip
do
  set -- "$@" --file "$file"
done
mycli "$@"

Or, assuming GNU tools:

find . -maxdepth 1 -name '*.zip' -printf '--file\0%f\0' |
  xargs -0 -- mycli

A relevant difference between the array-based approach and the xargs-based one: while the former may fail with an "Argument list too long" error (assuming mycli is not a builtin command), the latter will not, and will run mycli more than once instead. Note, however, that in this last case all but the last invocation's argument lists may end with --file (and the following one start with a file name). Depending on your use case you may be able to use a combination of xargs' options (e.g. -n and -x) to prevent this.

Also, note that find will include hidden files in its output, while the array-based alternatives will not, unless the dotglob shell option is set in Bash or, in a POSIX shell, both the *.zip and .*.zip globbing expressions are used. For details and caveats on this: How do you move all files (including hidden) from one directory to another?.

fra-san
  • 10,205
  • 2
  • 22
  • 43
  • Thanks so much for not only providing an answer, but also providing multiple ways as well as an explanation of the differences! Much appreciated :) – blitzmann Jan 25 '21 at 16:49
3

Assuming you don't have many thousands of files (this would generate an "Argument list too long" error), then you may do it like this with the zsh shell:

mycli ./*.zip(P:--file:)

Or, from the bash shell:

zsh -c 'exec mycli ./*.zip(P:--file:)'

(I'm using exec here to replace the zsh shell with the mycli process, for a bit of efficiency.)

The P filename globbing qualifier prepends the given string (here, --file) as a separate word before each element that the glob expands to.

To include hidden files, add D before P, and to limit the matching to only regular files, add .:

zsh -c 'exec mycli ./*.zip(.DP:--file:)'

For a recursive globbing down into subdirectories:

zsh -c 'exec mycli ./**/*.zip(.DP:--file:)'

... and to order the files in order of modification timestamp (would you want to do so), add om as you added D and . before.

Kusalananda
  • 333,661
2

this works:

ls *.zip | xargs printf "--file %s " | xargs echo mycli

Once you're satisfied with the resulting command, remove the echo in the second xargs and re-run.

If you have zip files whose name have spaces or other shell meta characters, it gets a bit more complicated though.