3

I have a bash script like:

files="${@:2}"
for f in $files; do
  echo $f
done

which takes the shell expansion from the second argument to the last, and print it. However it does not work with files with spaces. It prints files:

test image.jpg

as

test
image.jpg

If I use:

for f in "${@:2}"; do
  echo $f
done

without assigning it to $files, it works fine.

I also tried to use the first case with quoted "$files", and does not work. It prints all files in one line. How can I assign the file names to another variable, while still making it work when using expansion like for in loop?

SwiftMango
  • 151
  • 4

1 Answers1

3

To make that work, you need files to be an array, not a variable. Try:

files=("${@:2}")
for f in "${files[@]}"; do
  echo "$f"
done

Example:

$ bash script.sh first "test image.jpg" "another test.txt"
test image.jpg
another test.txt

Discussion

From the original code, consider this statement:

files="${@:2}"

This assigns all arguments after the first to files as a single undifferentiated string. We can see that with this script:

$ cat test.sh
files="${@:2}"
declare -p files
$ bash test.sh first "test image.jpg" "another test.txt"
declare -- files="test image.jpg another test.txt"

As you can see, all distinction between the second and third arguments has been lost.

John1024
  • 74,655
  • This works! The key is to use () around the elements in assignment and use $var[@] in the for-in loop... Thanks! Will accept this when allowed. – SwiftMango Oct 11 '15 at 19:24
  • Glad it worked. To be precise, it needs to be "${var[@]}". Both the double-quotes and the braces are important. – John1024 Oct 11 '15 at 19:30