why variable substitution is so different?

50 Views Asked by At

there are two files in current dir

[root@workspace tmp]# ll
total 8
-rw-------. 1 root root 6 Mar 31 13:18 *
-rw-------. 1 root root 6 Mar 31 13:18 ?

[root@workspace tmp]# for f in "`find . -type f`";do echo "$f";done
./*
./?

[root@workspace tmp]# for f in "`find . -type f`";do echo $f;done
./* ./? ./* ./?

I am confused about the output of the two echo ...

the differenct is: the double quote around the $f

why the outputs are so, anyone can explain this?

thanx any help in advance.

1

There are 1 best solutions below

2
Renaud Pacalet On BEST ANSWER

The shell expands the commands before executing them. There are 7 consecutive expansions: brace expansion, tilde expansion, parameter and variable expansion, command substitution, arithmetic expansion, word splitting, and pathname expansion (see this section of the bash manual).

In your case brace expansion, tilde expansion and arithmetic expansion have no effect because you do not use these features.

Before the for loop is executed command substitution replaces the find command with ./? ./*. Because it is enclosed in double quotes the word splitting does not split ./? ./* and filename expansion is skipped. The for command is thus not further transformed before execution. The loop iterates only once with value ./? ./* of variable f.

In echo "$f" $f is expanded (parameter expansion) and replaced with the value of variable f. Again, because of the double quotes, word splitting and filename expansion are skipped, the result is printed without further modifications, and you see ./? ./*.

In echo $f, $f is also expanded and replaced with the value of f but the expansion continues with word splitting and pathname expansion. Word splitting separates the two words ./? and ./* such that they are treated separately in the next steps. Pathname expansion replaces ./? with the list of all single-character file names in current directory (in your case ./? and ./*) and ./* is replaced with the list of all file names in current directory (in your case ./? and ./*). So, finally what is echoed is ./? ./* ./? ./*.