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 expands to a list with less or more than 1 element.
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. – Stéphane Chazelas May 04 '18 at 13:18case $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 04 '18 at 17:51