Differences between revisions 5 and 6
Revision 5 as of 2012-09-04 05:31:33
Size: 4734
Editor: ormaaj
Comment:
Revision 6 as of 2012-09-04 09:44:50
Size: 4866
Editor: ormaaj
Comment:
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
`nullglob` is a Bash shell option which modifies [[glob]] expansion such that if no file names are matched tby a patterns expand to zero arguments, rather than to themselves. `nullglob` is a Bash shell option which modifies [[glob]] expansion such that patterns that match no files expand to zero arguments, rather than to themselves.
Line 46: Line 46:
For instance, `unset var[1]` will suddenly "stop working". That's because bash thinks the `var[1]` is a glob, doesn't find a file that matches it, and per instruction of `nullglob` removes it, causing your script to run `unset` instead of `unset var[1]` - and nothing gets unset. The correct way to fix this issue is to quote the variable name (and always specify -v explicitly): `unset -v 'var[1]'`. Another example is defining an array using `arr=([1]=*)` where the right hand side of the assignment is an unquoted glob. This is a Bash bug which should be fixed in a future release, but it is still good practice to quote them. For instance, `unset var[1]` will suddenly "stop working". That's because bash thinks the `var[1]` is a glob, doesn't find a file that matches it, and per instruction of `nullglob` removes it, causing your script to run `unset` instead of `unset var[1]` - and nothing gets unset. The correct way to fix this issue is to quote the variable name (and always specify -v explicitly): `unset -v 'var[1]'`. Another example is defining an array using `arr=([1]=*)` where the right hand side of the assignment is an unquoted glob. If `nullglob` is enabled in this case then the result will be an empty array. This is a Bash [[http://lists.gnu.org/archive/html/bug-bash/2012-08/msg00032.html|bug]] which should be fixed in a future release, but it is still good practice to quote them.

nullglob is a Bash shell option which modifies glob expansion such that patterns that match no files expand to zero arguments, rather than to themselves.

Normally, when a glob which does not match any filenames is expanded, it remains unchanged. Thus, you get results like this:

  •  $ rm *.bak
     rm: cannot remove `*.bak': No such file or directory

The unmatched glob *.bak is not replaced with anything; it is passed to rm directly as if it were a literal filename. rm then tries to unlink it, and fails with a reasonable error message.

However, this causes some undesired results in programming. Consider an array populated like this:

  •  # Bash
     files=(*)
     echo "There are ${#files[*]} files here."

This fails if there are no files which match the glob -- it will report one file, because the array is loaded with the single element *.

The nullglob option lets us avoid this problem. If it is set, then an unmatched glob is swept away entirely -- replaced with a set of zero words -- instead of remaining in place as a single word.

  •  # Bash
     shopt -s nullglob
     files=(*)
     echo "There are ${#files[*]} files here."

nullglob is not on by default because there are other cases where its behavior would be extremely disconcerting. For example, ls behaves quite unexpectedly if an unmatched glob is removed from the argument list:

  •  shopt -s nullglob
     ls *.xyzqj
     # This will list all the files in the current directory
     # just as if the user had typed "ls" with no args.

nullglob by itself may not be sufficient for all cases. A glob does not, by default, match files whose names begin with a period, unless the glob itself also begins with a period. If so-called "hidden files" should also be matched by a glob, then one may wish to use the dotglob option.

  •  # Bash
     shopt -s dotglob
     shopt -s nullglob
     files=(*)
     echo "There are ${#files[*]} files here."

Just remember that these options remain in effect for the current shell process until disabled.

Warning

Enabling nullglob on a wide scope can trigger bugs caused by bad programming practices which might otherwise show no symptoms under most conditions.

For instance, unset var[1] will suddenly "stop working". That's because bash thinks the var[1] is a glob, doesn't find a file that matches it, and per instruction of nullglob removes it, causing your script to run unset instead of unset var[1] - and nothing gets unset. The correct way to fix this issue is to quote the variable name (and always specify -v explicitly): unset -v 'var[1]'. Another example is defining an array using arr=([1]=*) where the right hand side of the assignment is an unquoted glob. If nullglob is enabled in this case then the result will be an empty array. This is a Bash bug which should be fixed in a future release, but it is still good practice to quote them.

With very few exceptions (a small number of builtin commands which use modified parsing under special conditions, e.g. declare), Always use Quotes when arguments to simple commands could be interpreted as globs (words containing *, ?, and [, and extglob patterns if enabled). If you wish to detect cases where pathname expansion occurs in unintended places, you may enable the failglob option to make Bash exit with an error if a glob pattern fails to match any file names. However, you must then guarantee all globs that are really intended to be globs match at least one file. Also note that the result of a glob expansion isn't guaranteed to differ from the glob itself. failglob won't distinguish echo ? from echo '?' in a directory containing only a file named ?. nullglob will.

Enabling failglob, nullglob, or both during development and testing can help catch mistakes early.

Portability

"null globbing" is not specified by POSIX. In portable scripts, you must explicitly check that a glob match was successful by checking that the files actually exist.

# POSIX

for x in *; do
    [ -e "$x" ] || break
    ...
done

# POSIX

f() {
    [ -e "$1" ] || return 1

    for x; do
        ...
    done
}

f * || echo 'No files found'

Some modern POSIX-compatible shells allow null globbing as an extension.

# Bash
shopt -s nullglob

In ksh93, there is no toggle-able option. Rather, that the "nullglob" behavior is to be enabled is specified inline using the "N" option to the ∼() sub-pattern syntax.

# ksh93

for x in ~(N)*; do
    ...
done

# zsh
setopt NULL_GLOB

mksh doesn't yet support nullglob (maintainer says he'll think about it).

See Also

NullGlob (last edited 2016-05-08 15:03:21 by geirha)