14

How can I list files with a filename ending with last character and with .txt extension ?

I have tried ls *+([[:digit:]]).txt but this is true for abc12.txt and abc2.txt.

But I need to get only abc2.txt. How can I do that?

Is there any sort form of :digit: that will do this?

  • 2
    It should be noted pattern is only valid with extended globbing endabled: shopt -s extglob. – donothingsuccessfully Aug 4 '12 at 17:04
19

How about:

ls *[!0-9][0-9].txt

The ! at the beginning of the group complements its meaning.

As noted in the comments, this is bash's doing, try e.g.:

printf "%s\n" *[!0-9][0-9].txt
  • 2
    Note that bash (i.e. ls) does not support regular expressions here. These are filename expressions (Globbing). You can see the difference already in your example: here, * is a wildcard, which is not there in regular expressions, where you would use .* to achieve the same. Regular expressions are a lot more powerful than globbing. – Christopher K. Jun 5 '19 at 9:20
2

The question asked for regular expressions. Bash, and thus ls, does not support regular expressions here. What it supports is filename expressions (Globbing), a form of wildcards. Regular expressions are a lot more powerful than that.

If you really want to use regular expressions, you can use find -regex like this:

find . -maxdepth 1 -regex '\./.*[^0-9][0-9]\.txt'

Find is recursive by default, but ls is not. To only find files in the current directory, you can disable recursion with -maxdepth 1. Find matches against the filename with path, that's why the filenames start with ./ if you run find in .. The regex starts with \./ to cope with that. Note that in a regex, . is a special character matching any character, but here we really want a dot, that's why we escape it with a backslash. We do the same for the dot in .txt, because the regex would otherwise also match Atxt. The digit classes are same as for globbing, just that you need ^ instead of ! to invert the character class.

If you want to get the output of ls, then you can use -exec ls like this:

 find . -maxdepth 1 -regex '\./.*[^0-9][0-9]\.txt' -exec ls -lah {} \;

find supports a couple of different regex flavors. You can specify a -regextype, for example:

find . -maxdepth 1 -regextype egrep -regex '\./.*[^0-9][0-9]\.txt'

For me, possible types are: ‘findutils-default’, ‘awk’, ‘egrep’, ‘ed’, ‘emacs’, ‘gnu-awk’, ‘grep’, ‘posix-awk’, ‘posix-basic’, ‘posix-egrep’, ‘posix-extended’, ‘posix-minimal-basic’, ‘sed’ You can run find -regextype help to find out what is supported on your system.

0

With the ksh extended globs (or bash -O extglob or zsh -o kshglob) that you're already using, that would be:

ls -d -- ?(*[![:digit:]])[[:digit:]].txt

Or

ls -d -- !(*[[:digit:]])[[:digit:]].txt

If you want to match on a1.txt and 2.txt but not a12.txt nor 12.txt.

However note that in ksh and bash (unless you set the failglob option to get a behaviour similar to zsh's), if that pattern doesn't match any file, the pattern will be passed literally to ls, and if that (strangely named) file happens to exist, it will be listed by ls even though it doesn't match the pattern itself.

To include .2.txt, set the dotglob option in bash, add the (D) glob qualifier in zsh, or set FIGNORE to !(.|..) in ksh93.

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.