Search code examples
bashglob

Why does 'ls *[[:digit:]*]' work but 'ls *[[:digit:]*]_[[:digit:]*]' does not?


I modified my firewall rules and ufw created backups of the changed files. I wanted to make sure I didn't delete something by accident so I ran the following:

$ ls *[[:digit:]*]_[[:digit:]*]
ls: cannot access '*[[:digit:]*]_[[:digit:]*]': No such file or directory

I also tried

$ ls *_[[:digit:]*]
ls: cannot access '*_[[:digit:]*]': No such file or directory

What's wrong with the underscore here?

Finally, the following worked. But it would match any file that ended with any number of digits.

$ ls *[[:digit:]*]
after6.rules.20191223_104857  after.rules.20191223_104857  before6.rules.20191223_104857  
before.rules.20191223_104857  user6.rules.20191223_104857  user.rules.20191223_104857

In case this helps, my glob-related shopt settings are

$ shopt | grep glob
dotglob         on
extglob         on
failglob        off
globasciiranges on
globstar        on
nocaseglob      on
nullglob        off

Solution

  • You are assuming that [[:digit:]*] means "zero or more digits". This is not true. It matches a single digit or asterisk.

    What you intended was extglob *([[:digit:]]):

    ~/tmp $ ls **([[:digit:]])_*([[:digit:]])
    after.rules.20191223_104857   before.rules.20191223_104857  user.rules.20191223_104857
    after6.rules.20191223_104857  before6.rules.20191223_104857 user6.rules.20191223_104857
    
    ~/tmp $ ls *_*([[:digit:]])
    after.rules.20191223_104857   before.rules.20191223_104857  user.rules.20191223_104857
    after6.rules.20191223_104857  before6.rules.20191223_104857 user6.rules.20191223_104857