It's
vital in scripting to understand how the shell handles arguments and whitespace. Study these links for the details:
http://mywiki.wooledge.org/Arguments
http://mywiki.wooledge.org/WordSplitting
http://mywiki.wooledge.org/Quotes
The function of the
echo command is to print each and every argument it gets separately, with a single space between them. Since your text strings are unquoted the shell breaks the them up into their individual words
before echo processes them.
Next, if we look closely at your original scriptlet:
Code:
$ echo one two; echo three four | while read -e "fname"; do echo ${fname} ; done
one two
three four
The semicolon between the two
echos means you are running
two separate commands. The first echo executes and prints "one two" to
stdout, then the second
echo executes, sending its output into the
while loop, which prints it with the third
echo. So only "
three four" is actually processed by the loop. This can be avoided with
command grouping, or through the use of "
echo -e" to combine them into a single command, as pointed out by shivaa.
Next, you should be aware of the
variable scoping limitation when using pipes. The above will work as long as you are only echoing the results, but it wouldn't work if you needed to set variables that are still needed after the loop terminates.
The recommended
bash syntax is to instead feed the loop with a
process substitution or a
here doc/here string.
Code:
while read -r fname; do
echo "$fname"
done < <( echo -e 'one two\nthree four' )
while read -r fname; do
echo "$fname"
done <<<$'one two\nthree four'
As mentioned in the
quoting page I gave above, the special
$'..' quoting pattern acts similarly to
echo -e, expanding backslash characters.
The
-e option to
read is also superfluous if you aren't using it interactively, BTW. But it is recommended to always use the
-r option, particularly if the input text can contain literal backslashes.
Finally, be aware that
printf is often a better choice when you simply need to output your data on multiple lines, since it has implicit looping built into it.
Code:
printf '%s\n' 'one two' 'three four'