7

To identify files with the hyphen symbol - in file names such as test-19.1.txt, the find command combined with a regular expression does not appear to match.

The command find . -maxdepth 1 -regextype posix-egrep -regex '.*/[a-z0-9\-\.]+\.txt' -exec echo {} \; is run in a bash shell and no such file is discovered. If the hyphen is removed from the filename, the regular expression matches.

The same regular expression when tested with regexr.com is successful.

3
  • Apart from the fact that you have defined a range of characters between \ and \ in you expression, you could probably get away with two ordinary -name tests here, by using ! -name '*[!a-z0-9.-]*' -name '*.txt'. This would also make it portable to systems without GNU find. If you really just want to find names with - in them, use -name '*-*'. Jan 5, 2020 at 9:53
  • @Kusalananda - Which systems do not include find?
    – Ryan
    Jan 5, 2020 at 17:06
  • 5
    OpenBSD and other BSD systems (like macOS) does not include GNU find in their base systems. The native find implementation on those systems was not provided by the GNU project. The find on macOS does support -regex though, but not -regextype. Both -regex and -regextype are non-standard tests/options in find, while -name is not. Jan 5, 2020 at 17:55

2 Answers 2

16

To include a hyphen in a character class it must be at the first or last position

From find manual "the type of regular expression used by find and locate is almost identical to that used in GNU Emacs" and from Emacs manual:

  • [ ... ]
    • To include a ‘-’, write ‘-’ as the first or last character of the set, or put it after a range. Thus, ‘[]-]’ matches both ‘]’ and ‘-’.

So your regex should be '.*/[a-z0-9.-]+\.txt'

In POSIX BRE & ERE the same rule applies

The <hyphen-minus> character shall be treated as itself if it occurs first (after an initial '^', if any) or last in the list, or as an ending range point in a range expression. As examples, the expressions "[-ac]" and "[ac-]" are equivalent and match any of the characters 'a', 'c', or '-'; "[^-ac]" and "[^ac-]" are equivalent and match any characters except 'a', 'c', or '-'; the expression "[%--]" matches any of the characters between '%' and '-' inclusive; the expression "[--@]" matches any of the characters between '-' and '@' inclusive; and the expression "[a--@]" is either invalid or equivalent to '@', because the letter 'a' follows the symbol '-' in the POSIX locale. To use a <hyphen-minus> as the starting range point, it shall either come first in the bracket expression or be specified as a collating symbol; for example, "[][.-.]-0]", which matches either a <right-square-bracket> or any character or collating element that collates between <hyphen-minus> and 0, inclusive.

If a bracket expression specifies both '-' and ']', the ']' shall be placed first (after the '^', if any) and the '-' last within the bracket expression.

Regular Expressions

In fact most regex variants has the same rule for matching hyphen

The hyphen can be included right after the opening bracket, or right before the closing bracket, or right after the negating caret. Both [-x] and [x-] match an x or a hyphen. [^-x] and [^x-] match any character that is not an x or a hyphen. This works in all flavors discussed in this tutorial. Hyphens at other positions in character classes where they can’t form a range may be interpreted as literals or as errors. Regex flavors are quite inconsistent about this.

Character Classes or Character Sets

7
  • 1
    And in POSIX BREs and EREs, backslash is not special inside bracket expressions, so [\-\] matches characters between backslash and backslash like [a-f] matches characters between a and f. Jan 5, 2020 at 11:04
  • Note that the OP used -regextype posix-egrep, so they're not using the emacs type of regexps. The put it after a range won't work with posix-egrep (at least on GNU systems). Jan 5, 2020 at 11:05
  • @StéphaneChazelas thanks for the note. I added the quote for ERE
    – phuclv
    Jan 5, 2020 at 11:27
  • @phuclv - What difference is there between an emacs and posix-egrep regular expression?
    – Ryan
    Jan 5, 2020 at 17:07
  • @Ryan sorry I'm not a regex variant expert. I always use Perl regex because BRE and ERE are so confusing. If you're interested you should ask another question
    – phuclv
    Jan 5, 2020 at 17:18
5

Try;

find . -maxdepth 1 -regextype posix-egrep -regex '.*/[a-z0-9.-]+\.txt'

Inside a bracket expression:

  • A dash has the special meaning of a range. That special meaning is only avoided when the dash is either at the start (after an optional ^, if used) or at the end.
  • In general a backslash doesn't escape the next character, it is a literal \. In particular: there is no need to escape a dot nor there is a way to escape the special meaning of a dash (-) with a backslash.

So, what you wrote [a-z0-9\-\.] is understood as a range from \ to \ (or just a \).

0

You must log in to answer this question.

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