Why can't the wildcard expression be used as a non-root user?

In /var/log/apache2/*[0-9].gz the substring *[0-9] is handled by the pathname expansion mechanism of the shell. Not of sudo nor ls. Of the shell. This happens before sudo or ls even starts.

To expand /var/log/apache2/*[0-9].gz the shell needs to examine the content of /var/log/apache2/. In my Debian 10 the permissions are rwxr-x---, the ownership is root:adm. In effect the root's shell can examine the content but a regular user's shell cannot.

Therefore the pattern gets expanded for root, but it stays verbatim for a regular user. Neither sudo nor ls expand the pattern and eventually the regular user's (elevated) ls tries to list information about /var/log/apache2/*[0-9].gz exactly; there is no such file or directory.

This should work for a regular user:

sudo sh -c 'ls /var/log/apache2/*[0-9].gz'

In this case sudo will run elevated sh and this shell will successfully expand the pattern.

(At first I thought sudo -s … would do it, but no.)


Unlike Windows/DOS, globs are expanded by the shell before running the command in Unix systems. The shell in this case is running with your UID, not root, so it can't read that directory.

The default behaviour in the no-match case is to pass on the glob expression literally. e.g. if you run ls xyz*xyz in your normal shell, you'll see ls: cannot access 'xyz*xyz': No such file or directory from ls, because it got that string as an argument.

Just like if you'd run ls '*' to pass * as a literal string to ls. (Quote-removal is also the shell's job, so a command can't tell whether its arg was quoted or not.)

You can change this behaviour in bash, e.g. shopt -s failglob means the same ls xyz*xyz command will give bash: no match: xyz*xyz without invoking ls at all. (If you'd used sudo, you would have noticed that it didn't prompt you for a password before getting this error message.)

failglob produces the same "no match" error when trying to read a directory that you don't have read permission on, unfortunately not warning you about the EPERM error that bash go when it made an open system-call on the directory. bash: no match: /var/log/private/*

(There's also shopt -s nullglob, where a non-matching glob expression is removed instead of passed on literally. Then your sudo ls /...* command would have confusingly just run as sudo ls. This is perhaps useful for scripting where you might want to do *.gz *.tgz *.tar or something, and not get errors from the failed expansion. Probably not something you'd usually want for interactive use.)


You can use shopt -u failglob or whatever to unset these options in a shell, if you set them to try them out.