I want to write a
/bin/sh shell script which will handle any files matching a wildcard. It's easy to handle 1 or more matching files. However, I'm finding it awkward to handle the case of 0 matching files.
The obvious construct is:
#!/bin/sh for f in *.ext; do handle "$f" done
*.ext could be one or more expressions which the shell compares to file paths, and
handle is a shell command which runs correctly if given a path to an existing file, but fails if giving a path which does not map to a file. (In the case that provokes this question, they are
ffmpeg, but I think that doesn't matter.)
If there are matching files
bar.ext, then this script performs
handle "foo.ext" handle "bar.ext"
as expected. However, if there not any matching files, this script gives an error message like,
handle: *.ext: No such file or directory
I think I understand why this happens: the bash man page (which I assume is valid for
/bin/sh also) says the "list of words following
in is expanded, generating a list of items.… If the expansion of the items following in results in an empty list, no commands are executed, and the return status is 0." Apparently when
*.ext "is expanded", the list of results includes
*.ext, instead of an empty list. But that doesn't explain how to stop it happening.
(Update: There is a
sh (1) man page from the Heirloom Project which describes the Bourne Shell
sh, not the later Bourne Again Shell
bash. Under File Name Generation, it clearly says, "If no file name is found that matches the pattern then the word is left unchanged." It explains why
sh leaves a
*.ext pattern in the list.)
What is a compact, idiomatic way to write this loop so that it will run zero times with no errors if there are no matching files, but will run once for each matching file? Better yet, what will work with multiple patterns like:
for f in *.ext1 special.ext1 *.ext2; do ...
I prefer to use syntax compatible with
/bin/sh, for portability. I happen to be using Mac OS X 10.11.6, but I'm hoping for syntax which works on any Unix-like OS.
I came up with a couple of clumsy, non-idiomatic ways to write such a loop. If I don't immediately get good answers, I'll contribute those as answers, for the record.