2

This little setting drove me crazy:

shopt -s nullglob

I use it in my bash 4.3 script as a global setting to avoid errors scanning empty directories.

Now I found a strange problem which I do not understand:

# shopt -s nullglob
# array=(foo bar)
# echo "${array[@]}"
foo bar
# array=(foo bar?)
# echo "${array[@]}"
foo
# shopt -u nullglob
# array=(foo bar)
# echo "${array[@]}"
foo bar
# array=(foo bar?)
# echo "${array[@]}"
foo bar?

As you can see it removes the bar? from the array element, but why? The documentation refers to filename patterns which could include * and ?, but an array alone has nothing to do with "matching no files":

If set, Bash allows filename patterns which match no files to expand to a null string, rather than themselves.

Is this a bug?

P.S. I will set/unset nullglob around loops to solve my problem. This is not the question!

Update1

As @MichaelHomer asked for:

# shopt -s nullglob
# array=(*)
# echo "${array[@]}"
1 Video_folder Server bin config dev etc etc.defaults initrd lib lib32 lib64 lost+found mnt proc root run sbin storage sys tmp tmpRoot usr var var.defaults volume1
# shopt -u nullglob
# array=(*)
# echo "${array[@]}"
1 Video_folder Server bin config dev etc etc.defaults initrd lib lib32 lib64 lost+found mnt proc root run sbin storage sys tmp tmpRoot usr var var.defaults volume1

And this means?

mgutt
  • 467

1 Answers1

4

When you do array=(foo bar?), the bar? string is unquoted, and it contains the filename globbing character ? (which matches any single character). This means that the shell will perform filename globbing on this value. If the pattern does not match anything, it is by default left as it is. If it matches something (like bar1, bar-, bar. etc.), the matching names would be inserted into the array.

With nullglob enabled, the unquoted globbing pattern bar? is instead removed completely if there are no filenames matching it (this is usually the reason why one would want to enable nullglob). This means that your array, in this case, will only contain the foo element.

In other words, the nullglob setting would (unless you have a file called bar? in the current directory) prevent the unquoted word bar? from even being assigned as an element in the array.

The remedy, if you want the two strings foo and bar? in your array, is to quote them (foo does not need quoting, but it does not hurt to be consistent):

array=('foo' 'bar?')

Another way to "solve" this is to disable filename globbing. This is done through set -f:

set -f
array=(foo bar?)
set +f

What Michael probably wanted to show with array=(*) is that globbing happens for unquoted values (here, the unquoted *). He wanted to show this because this is the root of your issue.

Kusalananda
  • 333,661