Bash – How to Iterate File List Except When Empty

bashparameter

I thought this would be simple – but it is proving more complex than I expected.

I want to iterate through all the files of a particular type in a directory, so I write this:

#!/bin/bash

for fname in *.zip ; do
   echo current file is ${fname}
done

This works as long as there is at least one matching file in the directory. However if there are no matching files, I get this:

current file is *.zip

I then tried:

#!/bin/bash

FILES=`ls *.zip`
for fname in "${FILES}" ; do
    echo current file is ${fname}
done

While the body of the loop does not execute when there are no files, I get an error from ls:

ls: *.zip: No such file or directory

How do I write a loop which cleanly handles no matching files?

Best Answer

In bash, you can set the nullglob option so that a pattern that matches nothing "disappears", rather than treated as a literal string:

shopt -s nullglob
for fname in *.zip ; do
   echo "current file is ${fname}"
done

In POSIX shell script, you just verify that fname exists (and at the same time with [ -f ], check it is a regular file (or symlink to regular file) and not other types like directory/fifo/device...):

for fname in *.zip; do
    [ -f "$fname" ] || continue
    printf '%s\n' "current file is $fname"
done

Replace [ -f "$fname" ] with [ -e "$fname" ] || [ -L "$fname ] if you want to loop over all the (non-hidden) files whose name ends in .zip regardless of their type.

Replace *.zip with .*.zip .zip *.zip if you also want to consider hidden files whose name ends in .zip.

Related Question