An unquoted variable (as in
$var) or command substitution (as in
`cmd`) is the split+glob operator in Bourne-like shells.
That is, their content is split according to the current value of the
$IFS special variable (which by default contains the space, tab and newline characters)
And then each word resulting of that splitting is subject to filename generation (also known as globbing or filename expansion), that is, they are considered as patterns and are expanded to the list of files that match that pattern.
for i in $(xrandr), the
$(xrandr), because it's not within quotes, is split on sequences of space, tab and newline characters. And each word resulting of that splitting is checked for matching file names (or left as is if they don't match any file), and
for loops over them all.
for i in "$(xrandr)", we're not using the split+glob operator as the command substitution is quoted, so there's one pass in the loop on one value: the output of
xrandr (without the trailing newline characters which command substitution strips).
$i is unquoted again, so again the content of
$i is split and subject to filename generation and those are passed as separate arguments to the
echo command (and
echo outputs its arguments separated by spaces).
So lesson learnt:
- if you don't want word splitting or filename generation, always quote variable expansions and command substitutions
- if you do want word splitting or filename generation, leave them unquoted but set
$IFS accordingly and/or enable or disable filename generation if needed (
Typically, in your example above, if you want to loop over the blank separated list of words in the output of
xrandr, you'd need to:
$IFS at its default value (or unset it) to split on blanks
set -f to disable filename generation unless you're sure that
xrandr never outputs any
[ characters (which are wildcards used in filename generation patterns)
And then only use the split+glob operator (only leave command substitution or variable expansion unquoted) in the
in part of the
set -f; unset IFS
for i in $(xrandr); do whatever with "$i"; done
If you want to loop over the (non-empty) lines of the
xrandr output, you'd need to set
$IFS to the newline character: