1

I'm struggling with Bash variable expansion. Please see the following code:

~/tmp 689$ a=~/Library/Application\ *; echo $a
/Users/foo/Library/Application *

~/tmp 690$ echo ~/Library/Application\ *
/Users/foo/Library/Application Scripts /Users/foo/Library/Application Support

As the order of expansion is brace->tilde->parameter->....->pathname, why is pathname expansion not applied to $a in the same way that it is in the 2nd command?

[added]

Does whitespace escaping have hidden behaviour regarding the following output?

~/tmp 705$ a=~/Library/Application*; echo $a
/Users/foo/Library/Application Scripts /Users/foo/Library/Application Support
mklement0
  • 382,024
  • 64
  • 607
  • 775
Chul-Woong Yang
  • 1,223
  • 10
  • 17
  • Perhaps the un-quoted, un-escaped spaces you have in your paths on line 5? – MeetTitan Dec 19 '14 at 02:31
  • I don't think that's the problem since line 5 is the output of line 4. – Chul-Woong Yang Dec 19 '14 at 02:34
  • `~` expansion is special. Use `$HOME` there and it'll work. – Etan Reisner Dec 19 '14 at 02:34
  • It does not work, sadly: 694$ a=$HOME/Library/Application\ *; echo $a /Users/foo/Library/Application * 695$ echo $HOME/Library/Application\ * /Users/foo/Library/Application Scripts /Users/foo/Library/Application Support – Chul-Woong Yang Dec 19 '14 at 02:36
  • 2
    @EtanReisner: `~` actually _is_ expanded when used unquoted in assignments; i.e., _tilde_ expansion _does_ take place, but _pathname_ expansion _doesn't_, and the latter is the problem. As a potentially interesting aside: unquoted `~` is not only expanded as the _first_ char., but also after [unquoted] `:` chars. (to help with defining `$PATH`-like variables); e.g. `a=~:~` – mklement0 Dec 19 '14 at 04:39

1 Answers1

8

To do what you meant to do, you'd have to use the following:

a=(~/Library/Application\ *)  # use an *array* to capture the pathname-expanded results
echo "${a[@]}"                # output all array elements (without further expansion)

As for why your code didn't work:

In the context of variable assignment involving only literals or string interpolation (references to other variables), NO pathname expansion takes place, even with unquoted strings (e.g., a=*, a="*", and a='*' all assign literal *)[1].

(By contrast, pathname expansion is applied to unquoted strings inside an array definition (e.g., a=(*), or inside a command substitution (e..g, a=$(echo *)).)

Thus, the literal content of $a is /Users/foo/Library/Application *

Executing echo $a - i.e., NOT double-quoting the variable reference $a - then applies word splitting and does the following:

  • it prints literal '/Users/foo/Library/Application' (the 1st word - no expansion applied, due to its contents)
  • it prints the pathname expansion applied to * (the 2nd word - i.e., it expands to matching filenames in the current dir.)

The fact that the latter results in * in your case implies that you happen to be running the echo command from an empty directory (save for hidden files, assuming the default configuration).


[1]Whether the string is unquoted or not does, however, matter with respect to tilde expansion; e.g., a=~ expands ~ to the user's home directory, whereas a='~' or a="~" assign literal ~.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    @cwyang: Glad to hear it. It's easy to get confused in these matters; understanding all the intricacies of shell string processing and expansions is no small feat. – mklement0 Dec 19 '14 at 02:52