3

Bash doesn't perform word splitting in globbing in these cases:

  • LHS or RHS of an assignment, except for indexed arrays
    var=$value                                # simple variable
    declare -A hash
    key="key with a space"
    hash[$key]=$value                         # index of an associative array
    
    arr=$(echo "1 2 3")                       # word splitting does happen here
    
  • Inside [[ ]]
    var="one two"
    if [[ $var = *" "* ]]; then ...           # check if var has a space in it
    if [[ $(echo "one two") = $var ]]; then   # use the output of command substitution to compare with var
    
  • Inside (( ))
    ((sum = $(echo "99 + 1")))                  # assigns 100 to sum
    
  • In herestring
    cat <<< *                                 # gives '*' as the output
    

Is there a definite list of cases where Bash does or doesn't perform word splitting and globbing?

6
  • cat <<< $var does word splitting (followed by joining with space) but not globbing in older versions. For redirections, that depends on whether the posix/sh mode is enabled and the shell is interactive or not. May 4 '18 at 13:18
  • Note that "indexed array" is the retronym for ordinary arrays, in contrast to associative array. You are talking about the index to an associative array.
    – chepner
    May 4 '18 at 17:43
  • 1
    case $foo in is another. I'm not sure about arguments to parameter expansion; foo="a * b" survives unexpanded in both "${bar:-$foo}" and ${bar:-"$foo"}. I don't recall the details, but I think there is a comment in the source to the effect that the argument itself is not subject to word-splitting or pathname expansion.
    – chepner
    May 4 '18 at 17:51
  • I asked the same question on StackOverflow to see if it gets more attention there. May 25 '18 at 23:30
1

That's typically the cases where it can't or it wouldn't make sense, so the non-list contexts. There are however non-list contexts where it does it, but complains when it results in more than one item, or joins those items with spaces.

Also, it's important to make the distinction between just wildcard pattern matching and filename generation or globbing, which is the generation of a list of file names that match a pattern.

For instance in [[ foo = * ]], there is no globbing, as in that * is not expanded to the list of non-hidden files in the current directory, but that * is still interpreted as a pattern (here it returns true as foo matches the * pattern).

By splitting here, we're referring to the implicit splitting that is done upon unquoted parameter expansion ($param), command substitution ($(...) and `...`), and arithmetic expansion ($((...)) and $[...]), using the $IFS special parameter in list contexts.

We'll take * as an example below. As a pattern, it matches any sequence of characters. As a glob it expands to all the non-hidden files in the current directory (subject to dotglob, GLOBIGNORE...).

The below applies to bash, there are variations in other shells.

Cases where splitting and globbing don't occur:

  • when quoted (with '*', "*", \*, $'*', $"*").

  • inside here documents (whether the delimiter is quoted or not):

    cat << EOF
    *
    EOF
    cat << 'EOF'
    *
    EOF
    
  • inside arithmetic expressions:

    • echo $((2 * 2)) (* is not globbed but $((...)) undergoes split+glob, try after IFS=4)
    • array[2 * 2]=4 / ${array[2 * 2]} / exec {array[2*2]}>&1. Beware that you need the quotes in unset -v 'a[1]' ([1] is a wildcard).
    • ((2 * 2))
    • echo $[2 * 2]
  • scalar variable assignment:

    • var=*
    • array[x]=*
    • hash[key]=*
    • array=([1]=*) (older versions of bash used to do globbing there though and do something different when there was a file called 1=foo in the current directory for instance).
    • var+=*
  • in associative array keys:

    • typeset -A hash; hash[**]=value; v=${hash[**]}. * and @ are special though.
  • in assignments after export/local/typeset/declare/readonly under some circumstances only: the assignment keyword and the variable name and = must not be quoted even in part, and not be the result of any expansion. assignments and redirections may occurs before, but command can't be used.:

    • OK (no split+glob):
      • export a=*
      • x=1 < /dev/null export foo a=*
    • not OK (split+glob performed):
      • ""export a=*
      • command export a=* (except in POSIX mode)
      • export "a"=*
      • export a\=*
      • "$(echo export)" a=*

    more on that at Are quotes needed for local variable assignment?

  • case * in (...); esac

  • case x in (*); esac (no split+glob, but that * is treated as a pattern, that also applies to wildcard found inside unquoted expansions as in var=*; case x in ($var)).

  • inside [[...]]. Though note that pattern matching is done if unquoted wildcards are present on the right hand side of =, ==, != operators there.

  • in here strings since version 4.4. In earlier versions, splitting (though not globbing) was done and the resulting words joined with spaces.

  • in the target of redirections when the shell is in POSIX mode and non-interactive: bash -o posix -c 'echo test > *. Otherwise, split+glob will be performed and bash will report an error if that expanded to a list with less or more than 1 element.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.