14

So I've got the following files in the tmp directory:

 file.0
 file.1
 file.t9
 file.22
 file.4444

if I wanted to list only the files that end in '.digits' (0, 1, 22, 4444) but not (t9) I could try and use wildcards such as this:

 ls tmp/file.{[0-9],[0-9][0-9],[0-9][0-9][0-9],[0-9][0-9][0-9][0-9]}

however I get the following results with the ugly error

 ls: cannot access tmp/file.[0-9][0-9][0-9][0-9]: No such file or directory
 file.0
 file.1
 file.22
 file.4444

I've also tried using {0..999} but that also results in the same sorts of errors (and a lot more of them). Any clues as how to do this without errors from the experts?

Rafe
  • 512
  • 1
  • 4
  • 15
  • 1
    Check this too http://stackoverflow.com/questions/15345936/regular-expression-usage-with-ls – Suvarna Pattayil Dec 04 '13 at 06:35
  • Brace expansion is different from pattern matching. It generates a list of explicit arguments, rather than a pattern to match whatever might be present. – chepner Dec 05 '13 at 14:38

4 Answers4

32

At least in bash 4, when the extglob shell option is enabled:

shopt -s extglob
ls /tmp/file.+([0-9])

The pattern +([0-9]) there matches one or more digits.

You can read more about this in the Pattern Matching section of man bash.

UPDATE

Actually, as @chepner pointed out, since extglob was introduced in version 2.02, this should work in pretty much every bash you come across today, unless you pulled your distro out of a rock or something.

janos
  • 120,954
  • 29
  • 226
  • 236
1

You can use ls just to list all the files then filter the output of that through grep:

ls -1 | grep -E '\.[0-9]+$'

as per the following test:

pax> printf 'file.0\nfile.1\nfile.t9\nfile.22\nfile.4444\n' | grep -E '\.[0-9]+$'
file.0
file.1
file.22
file.4444

The -E gives you extended regular expressions so that the + modifier works. If that's not available to you, you can use the \.[0-9][0-9]*$ regex instead.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
1

Another variant for filtering files of a specific extension is to use the find command with a set of predicates:

find tmp/ -type f -iregex '^.*\.[0-9]+$'

The -type predicate matches only files and the -iregex matches names that end with one or more digits - ignoring case of the name. If you want to filter files that begin with file you would use the following instead:

find tmp/ -type f -iregex '^.*/file\.[0-9]+$'

And finally, if you don't want the whole path displayed for each resulting file, use the following:

find tmp/ -type f -iregex '^.*/file\.[0-9]+$' -printf "%f\n"
0

Another simple solution to just prevent seeing (ie hide) "ugly errors" is to redirect standard error to /dev/null by appending 2> /dev/null. In your example:

ls tmp/file.{[0-9],[0-9][0-9],[0-9][0-9][0-9],[0-9][0-9][0-9][0-9]} 2> /dev/null

This would give you the results you want and hide any errors. Works with any linux command or executable that sends output to stdout and errors to stderr. Note that this does not affect the command return status ($?), it just suppresses errors.

Nic3500
  • 8,144
  • 10
  • 29
  • 40
James M.
  • 1
  • 1