Skip to content

Latest commit

 

History

History
51 lines (34 loc) · 1.79 KB

SC2044.md

File metadata and controls

51 lines (34 loc) · 1.79 KB

Pattern: Use of for loop over find output

Issue: -

Description

for var in $(find ...) loops rely on word splitting and will evaluate globs, which will wreck havoc with filenames containing whitespace or glob characters.

find -exec for i in glob and find+while do not rely on word splitting, so they avoid this problem.

Example of incorrect code:

for file in $(find somedir -mtime -7 -name '*.mp3')
do
  let count++
  echo "Playing file no. $count"
  play "$file"
done
echo "Played $count files"

This will fail for filenames containing spaces and similar, such as Some File.mp3, and has a series of potential globbing issues depending on other filenames in the directory like (if you have SomeFile2.mp3 and SomeFile[2014].mp3, the former file will play twice and the latter will not play at all).

Example of correct code:

There are many possible fixes, each with its pros and cons.

The most general fix (that requires the least amount of thinking to apply) is having find output a \0 separated list of files and consuming them in a while read loop:

while IFS= read -r -d '' file
do
  let count++
  echo "Playing file no. $count"
  play "$file"
done <   <(find somedir -mtime -7 -name '*.mp3' -print0)
echo "Played $count files"

In usage it's very similar to the for loop: it gets its output from a find statement, it executes a shell script body, it allows updating/aggregating variables, and the variables are available when the loop ends.

It requires Bash, and works with GNU, Busybox, OS X, FreeBSD and OpenBSD find, but not POSIX find.

Exceptions

If you know about and carefully apply IFS=$'\n' and set -f, you could choose to ignore this message.

Further Reading