0

I have directory work_dir, and there are some subdirectories inside. And inside subdirectories there are zip archives. I can see all zip archives in terminal:

find . -name *.zip

The output:

./folder2/sub/dir/test2.zip
./folder3/test3.zip
./folder1/sub/dir/new/test1.zip

Now I want to concatinate all these file names in single row with some option. For example I want single row:

my_command -f ./folder2/sub/dir/test2.zip -f ./folder3/test3.zip -f ./folder1/sub/dir/new/test1.zip -u user1 -p pswd1

In this example:
my_command is some command
-f the option
-u user1 another option with value
-p pswd1 another option with value

Can you help me please, how can I do this in Linux BASH ?

alex_t
  • 71
  • 1
  • 6
  • A fundamental but possibly important corner case is what would happen if the resulting command line is too long. Is it acceptable to run `mycommand` multiple times if required? Like if your files are "foo.zip", "bar.zip", and "baz.zip", you would run `mycommand -f foo.zip -f bar.zip etc` and then `mycommand -f baz.zip etc` in a separate invocation if your maximum allowed command line length was very very short. – tripleee Sep 01 '22 at 07:01
  • https://stackoverflow.com/a/71104113/874188 has some discussion around this but it's not really a duplicate per se (not least because the OP never completely settled on what worked for them). – tripleee Sep 01 '22 at 07:01

3 Answers3

1

One way is: (updated per @M. Nejat Aydin comments)

find . -name "*.zip" -print0 | xargs -0 -n1 printf -- '-f\0%s\0' | xargs -0 -n100000 my_command -u user1 -p pswd1

Note that -n100000 parameter forces all output of the previous xargs to be executed on the same line with the assumption that number of findings will be less than 100000.

I used null terminated versions (notice: -0 flag, -print0) because file names can contain spaces.

1

This is a bash script that should do what you wanted.

#!/usr/bin/env bash

user=user1
passwd=pswd1

while IFS= read -rd '' files; do
  args+=(-f "$files")
done < <(find . -name '*.zip' -print0)

args=("${args[@]}" -u "$user" -p "$passwd")

##: Just for the human eye to see the output, 
##: change this line of code according to the comment below.   
printf 'mycommand %s\n' "${args[*]}" 

The output should be in one-line, like what you wanted, but do change the last line from

printf 'mycommand %s\n' "${args[*]}"

into

mycommand "${args[@]}"

If you actually want to execute mycommand with the arguments.


Change the value of user and passwd too.


  • A while + read loop was used with IFS.

See How can I read a file (data stream, variable) line-by-line (and/or field-by-field)?


  • Why the last line should be change. See Arguments

  • Shell quoting is a basic but common mistake when dealing with spaces in file/path name.

See How can I find and safely handle file names containing


Also the find command/utiliy.


  • The construct "${args[@}" is an array.

See Array1 Array2 Array3

Jetchisel
  • 7,493
  • 2
  • 19
  • 18
0

You can do this by making a bash script.

  1. Make a new file called whatever.sh
  2. Type chmod +x ./whatever.sh so it becomes executable on the terminal
  3. Add the BASH scripting as shown below..
#!/bin/bash
# Get all the zip files from your FolderName
files="`find ./FolderName -name *.zip`"

# Loop through the files and build your args
arg=""
for file in $files; do
    arg="$arg -f $file"
done

# Run your command
mycommand $arg -u user1 -p pswd1
Andrew Fenn
  • 107
  • 6
  • 2
    This is running into https://mywiki.wooledge.org/BashFAQ/050 – tripleee Aug 31 '22 at 11:21
  • thank you @AndrewFenn . This is the simpliest and best solution! – alex_t Aug 31 '22 at 14:11
  • This will fail if a filename contains any whitespace character. – M. Nejat Aydin Aug 31 '22 at 15:25
  • 1
    It will fail in quite a few ways. My aim with the answer wasn't to provide an exhaustive and comprehensively battle tested script. Just to provide enough information for them to get started to be able to further go and develop it themselves. – Andrew Fenn Aug 31 '22 at 18:39