0

I'm trying to use the find command's regex to match newlines using the dot character. This usually is accomplished via the s modifier; however, different regex engines have different formats and I can't figure out exactly how to write the regex so that the find command will match newlines. For example, how would I change the following simple command to match all files including those containing newlines in their names:

find /home/user -regex ".*"

I've tried "(?s).*", "/.*/s", and probably a few others.

2 Answers 2

2

My version of GNU find supports a few different regex variants, which can be chosen with -regextype:

valid types are ‘findutils-default’, ‘awk’, ‘egrep’, ‘ed’, ‘emacs’, ‘gnu-awk’, ‘grep’, ‘posix-awk’, ‘posix-basic’, ‘posix-egrep’, ‘posix-extended’, ‘posix-minimal-basic’, ‘sed’.

Almost all of those let the dot match a newline, ...except emacs, which is the default.

So, use some other one. posix-extended is probably sane enough:

$ touch hello $'new\nline'
$ find . -type f -regex '.*'
./hello
$ find . -type f -regextype posix-extended -regex '.*'
./hello
./new?line

If you're running some other find, your mileage may vary. On a quick test, the one on my Mac does match newlines with . by default.

Then again, for simple patterns, you could just use -name which takes shell globs (and shouldn't have issues with newlines).

The (?s) looks like a feature of PCRE/Perl regexes, and I don't see find supporting that. As for /.*/s, find takes just the raw pattern, no separators, and hence no trailing options.

2
  • What's the point of -regex '.*' Can't you just not use -regex instead?
    – Kusalananda
    Commented Sep 1, 2021 at 20:41
  • @Kusalananda, they appear to be asking exactly about how to get the dot in a regex to match newlines too. Of course -regex '.*' is a bit silly in itself, any other regex would do.
    – ilkkachu
    Commented Sep 1, 2021 at 20:46
-2

The command

find ~user

would find all names, including those containing newlines. Add -type f if you want to only find regular files.

If you don't want to restrict the matching of names, don't use a test on the names.

To restrict the names matched using substrings, you could use a globbing pattern with the -name test. Both * and ? matches newlines in a globbing pattern, both in the shell in general and in the -name test of find:

Example using standard find:

$ find . -name 'file?name'
./file
name
$ find . -name 'file*name'
./file
name
$ find . -name 'd*name'
./dir
name
$ echo ./d*name
./dir
name

Answer to misunderstood question ("How may I find names containing newlines"):

Using standard find:

find ~user -name '*
*'

Using "C-strings" in shells that support these (still using standard find)

find ~user -name $'*\n*'

None of the built-in regular expression types normally available in GNU find is able to match a newline with \n, so you're left using a literal newline, just like in the globbing patterns above. This means it's probably easier to use a standard -name test than a non-portable -regex test.

6
  • 2
    You seem to have misunderstood the question: it's to match names that may contain a newline, not match that must contain a newline. With GNU find, find -regex '.*' does not match file names containing newlines. Commented Sep 1, 2021 at 20:35
  • @Gilles'SO-stopbeingevil' Thanks. Updated.
    – Kusalananda
    Commented Sep 1, 2021 at 20:38
  • The updated answer works for the specific toy example in the question, but isn't generalizable to any other case. Commented Sep 1, 2021 at 20:40
  • @Gilles'SO-stopbeingevil' It's difficult to see what cases they need covered and what doesn't work. It's a literal solution to their actual question.
    – Kusalananda
    Commented Sep 1, 2021 at 20:42
  • @Kusalananda, they're asking "how to write the regex so that [it] will match newlines", and mentioning (?s) and .../s, both of which are ways in Perl for enabling the s option to the regex. By default, the . in Perl doesn't match newlines, but with the s option it does. Try e.g. perl -e 'print "yes\n" if "foo\nbar" =~ /foo.bar/' vs. =~ /foo.bar/s or =~ /(?s)foo.bar/'
    – ilkkachu
    Commented Sep 1, 2021 at 20:53

You must log in to answer this question.

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