I am using a wildcard expression to match a filename.

This is what happens with a normal, non-root user:

debian@MiWiFi-R3-srv:~$ sudo ls  /var/log/apache2/*[0-9].gz
ls: cannot access '/var/log/apache2/*[0-9].gz': No such file or directory

And this is what happens after I switch to the root user with sudo su and try the same command:

root@MiWiFi-R3-srv:/home/debian# ls  /var/log/apache2/*[0-9].gz
/var/log/apache2/access.log.10.gz  /var/log/apache2/error.log.10.gz
/var/log/apache2/access.log.11.gz  /var/log/apache2/error.log.11.gz
/var/log/apache2/access.log.12.gz  /var/log/apache2/error.log.12.gz

Why can this wildcard expression be used as root, but not as a normal, non-root user?

  • The expression you are using is wildcard, since ls doesn't support Regular expressions. – Wasif Hasan Aug 6 at 1:41
  • sudo ls does not support ,ls support? – showkey Aug 6 at 1:43
  • No sudo does nothing but just elevates an executable or command / shell function. Here actually the executable used is ls and since ls does not use regex then °sudo ls` will not also. Both use wildcards. And you don't need to bother about this, as the expression you are even using is wildcard. Here * means any character (in regex it meant 0 or more instances of previous character), [0-9] means any character in range of 0 to 9 (which is same as regex). And if you used regex you would need to escape . using backslash because it means any character in regex. – Wasif Hasan Aug 6 at 1:45
  • If it works as you say, sudo su && ls /var/log/apache2/*[0-9].gz should show nothing. – showkey Aug 6 at 1:51
  • 3
    Does ls /var/log/apache2/ (without the globbing) work from your regular user account? – Spiff Aug 6 at 2:52

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.)

| improve this answer | |
  • 9
    It helps to understand that *[0-9].gz, even though unusual, is a perfectly legitimate filename on Unix/Linux. It could exist. So when ls is asked if that file exists, it must check that exact name. – MSalters Aug 6 at 15:14

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.

| improve this answer | |

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.