This is a common misconception coming from old bash versions or old shells altogether. When executing loops over file names, the
for loop has direct support for path expansion which means you don’t have to use
ls in a subshell to obtain the same result.
The form using a subshell
This form is very commonly used, even though it uses a subshell and it’s less visually appealing – obviously, this is personal opinion.
for FILE in `ls /etc/*rel*`; do ... $FILE ... ; done
Spawning a subshell that executes
ls can be expensive and most probably the reason why this form is so common is because there is little knowledge of the bash-only alternative.
Bash-only for loop over file names
In the vast majority of the times you need to use
ls, the following form works just as well:
for FILE in /etc/*rel*; do ... $FILE ... ; done
Whatever wildcard filter you’re passing to
ls, works with bash too. Mostly because before it’s passed to ls, bash expands
/etc/*rel* to the actual filenames that match it.
This form of looping over file names uses the bash feature called pathname/filename expansion. I also use it very often with arrays, such as:
ARR=( /etc/*rel* )
and then further process the
The nullglob case
By default, the nullglob feature is not enabled in bash. This causes the unmatched patterns to be sent as-is as input to the bash constructs. To avoid this, set nullglob in advance, usually at the beginning of your script:
shopt -s nullglob
So whenever a pattern doesn’t match any existing file, it will be expanded to null, which means a for loop won’t execute at all.
I’m done judging which form is better. In many cases, not having to spawn a new subshell can translate to more stability when the machine is under load. In some other cases, it looks clearer without the backquotes and the
However, if you were using the ls form before, just know that there is a bash-only alternative to executing a loop over file names.
A more in-depth article on this topic is practical pathname expansion.