1

I'm getting quite confused with Linux find command's regular expression usage.

I'm aware that there is an option regextype, but without that, according to the current man page, it is supposed to use Emacs regular expressions. This page seems to say that character classes are supported ("this is a POSIX feature"), but my experiments seem to show that nothing like [[:ascii:]] or [[:digit:]] or [[:alnum:]] ever works, quite apart from the fact that these are truly archaic ways of handling characters classes. Instead you seem to have to use [a-zA-Z] which, apart from anything else, is useless for Unicode characters.

So I turned to regextype: I find that you get a list of possible settings by going find -regextype help. This gives:

find: Unknown regular expression type ‘help’; valid types are ‘findutils-default’, ‘awk’, ‘egrep’, ‘ed’, ‘emacs’, ‘gnu-awk’, ‘grep’, ‘posix-awk’, ‘posix-basic’, ‘posix-egrep’, ‘posix-extended’, ‘posix-minimal-basic’, ‘sed’.

... so I assumed that by including -regextype posix-basic, for example, I'd be able to run something like this:

find . -maxdepth 1 -regextype posix-basic -regex .*\d.*

This produces results, but not the ones I was hoping for: all the files and folders in the current directory with the lower-case letter "d" in their names! I was expecting all names with at least one digit.

I've looked at quite a lot of Linux find regex questions here on Stack Exchange, but I don't think I've seen a single one where "modern" character class handling is demonstrated. Is any of the regextype options able to handle something like this:

find . -maxdepth 1 -regextype ??? -regex '.*\d{3}\s+.*'

where I mean "contains three digits followed by one or more empty space characters". I.e. something like regex rules from a normal language like Java, Python, Javascript, etc...?

later, following comments

Here's an exercise: make a directory and put a few files into it with random names. Then added files with the following names: 'ctb117b', 'ctb117c', 'trt117a'.

I then want to isolate the '117' files. There may be files called 'xxx0009333qqq'. So using a modern regex engine I'd go like this, for example (allowing for the preceding ./):

find . -regex './\w{3}\d\{3}.*' 

Using these more venerable Linux regex rules, what do I put that works?

find . -regextype posix-basic -regex '.*[[:digit:]]{3}.*' 

produces nothing. Nor does '.*[[:digit:]]+.*', for example. If anyone's sufficiently interested, please show me something which works for you (lists the above files).

12
  • \d is a Perl-like expression, also supported by some GNU tools as a short way of writing what would be written as [[:digit:]] in a POSIX expression. Same for \s ([[:blank:]]). The {3} modifier is a POSIX extended expression modifier.
    – Kusalananda
    Commented Jan 7, 2020 at 20:34
  • When using *, be sure to quote it. find . -maxdepth 1 -regextype posix-basic -regex .*\d.* probably gave you unexpected results because *\d.* matched something in the current directory, so the shell expanded it before find ever saw your regex. Commented Jan 7, 2020 at 20:35
  • [[:digit:]] works with all posix-* -regextype, and also with other (egrep, etc).
    – user313992
    Commented Jan 7, 2020 at 20:36
  • You could use -name '*[[:digit:]][[:digit:]][[:digit:]][[:blank:]]*'
    – Kusalananda
    Commented Jan 7, 2020 at 20:36
  • @AndyDalton I just tried that command with first single quotes and then double quotes. Both returned the same (wrong) results. Commented Jan 7, 2020 at 20:38

2 Answers 2

1

I would recommend using this :

find . -maxdepth 1 -regextype posix-extended -regex '.*[[:digit:]]{3}\s+.*'
12
  • Thanks. I tried it. Firstly it produced no results, but that wouldn't be surprising, as there are no spaces in the names of the files (see suggested exercise). If I drop the \s+ things get listed. But explain this: if I put '.*[[:digit:]]{80}.*' it returns exactly the same files! It completely ignores the "number of matches" directive! Commented Jan 7, 2020 at 20:59
  • In your post, you said "contains three digits followed by one or more empty space characters". Are you sure you get same files when you put '.*[[:digit:]]{80}.*' and -regextype posix-extended ?
    – Philippe
    Commented Jan 7, 2020 at 21:02
  • Sorry, no... I've got that wrong. The {80} directive is working as expected. Apologies. By the way, are you saying that backslash character classes work with posix-extended? So should "\w" work? I find that it does... but './\w{3}\d{3}.*' doesn't... I must find the right documentation for posix-extended obviously! Commented Jan 7, 2020 at 21:07
  • \w does not work with posix-extended
    – Philippe
    Commented Jan 7, 2020 at 21:09
  • 1
    Nitpick: \w, \d, \s etc. are not POSIX extended regular expression things, but GNU's implementation of POSIX extended regular expressions includes them (for convenience). They are originally from Perl.
    – Kusalananda
    Commented Jan 7, 2020 at 21:23
0

I believe the basic mistake in all the attempts is that the -regex primary checks the entire path, not only the final file name like -name does.

Therefore, if you want to check on the last part (component) of the path, you need to prepend .*/ to your search pattern, so that you match any parent path for the file name you want to find.

In other words:

find . -name 'filename'

is equivalent to:

find . -regex '.*/filename'

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .