Home > Enterprise >  For-loop over glob returns single string instead of list
For-loop over glob returns single string instead of list

Time:08-06

I'm trying to loop over images in a directory, but instead of one image per loop, my for-loop only iterates once and the loop variable contains all the images.

#!/bin/bash

IMAGEDIR="/home/user/myimages/"

for file in "$IMAGEDIR*.jpg"; do 
  echo $file
  echo "next file"
done

Outputs:

/home/user/myimages/A.jpg /home/user/myimages/B.jpg /home/user/myimages/C.jpg
next file

I would expect it to output:

/home/user/myimages/A.jpg 
next file
/home/user/myimages/B.jpg 
next file
/home/user/myimages/C.jpg
next file

I've looked at several other questions, and I don't think I should need a nested for loop.

Additionally, I figured out that it works if I use a slightly different format

#!/bin/bash

IMAGEDIR="/home/user/myimages/"

for file in "$IMAGEDIR"*.jpg; do 
  echo $file
  echo "next file"
done

Why does the first glob return a single string and the second one return a list?

CodePudding user response:

Why does the first glob return a single string and the second one return a list?

Because quotation suppresses pathname expansion, but quote removal leaves your glob subject to expansion later.

Consider your first example ...

for file in "$IMAGEDIR*.jpg"; do 
  echo $file
  echo "next file"
done

Parameter expansion applies within double quotes, so it expands "$IMAGEDIR*.jpg" to "/home/user/myimages/*.jpg". The quoting suppresses the significance of * for pathname expansion, however, and no other expansions apply, so "/home/user/myimages/*.jpg" is the final expanded version. Which is then subject to quote removal, so the first and only value that file take in that loop is (literally) /home/user/myimages/*.jpg.

In the body of the loop, then, parameter expansion expands echo $file to echo /home/user/myimages/*.jpg. The * is not quoted now, and pathname expansion happens after parameter expansion, so you get one echo command with the names of all the matching files as arguments (supposing that there are any matching files).

The difference in your second example is the placement of the quotation marks. With the * outside the quotation marks in the for line, you get pathname expansion there, resulting in several different values for variable file to take on successive iterations of the loop.

  •  Tags:  
  • bash
  • Related