1

I would like to use find perl-style regular expressions when using the find command to search for files. Currently it doesn't seem like this is supported.

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

Is there any way to add the perl regular expression engine to the find command?

I want to use it with find natively; I do not want to pipe the output of find to grep or another program.

4
  • No, not possible as a user. If you are developer, maybe try to fork find sources to include PCRE, but it seems a bit rude for one person. Commented Nov 30, 2022 at 20:55
  • 2
    You might want to ask a new question, explaining what you are trying to do. While find does not support PCREs, there are many things you can do with ERE or other regex flavors and you can also pass the output o find to tools that do support PCREs, so if you explain what the final objective is, we should be able to give you a solution.
    – terdon
    Commented Nov 30, 2022 at 21:26
  • The only way without pipe is to use grep -P in find . -exec grep -P 're' {} + Commented Dec 1, 2022 at 0:07
  • 1
    @GillesQuenot that would search through the contents of the files, not their names.
    – terdon
    Commented Dec 1, 2022 at 6:12

5 Answers 5

2

One way to use only one process, is to use only pure Perl with File::Find module.

It's included in the core since perl5, so you don't have anything to install:

$ corelist File::Find

Data for 2021-05-20
File::Find was first released with perl 5

There's a find2perl script out there.

You can generate the Perl code from your usual find commands:

find2perl [paths] [predicates] 

Ex:

$ find2perl . -type f -name '*l'
#! /usr/bin/perl -w
    eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
        if 0; #$running_under_some_shell

use strict;
use File::Find ();

# Set the variable $File::Find::dont_use_nlink if you're using AFS,
# since AFS cheats.

# for the convenience of &wanted calls, including -eval statements:
use vars qw/*name *dir *prune/;
*name   = *File::Find::name;
*dir    = *File::Find::dir;
*prune  = *File::Find::prune;

sub wanted;
   
# Traverse desired filesystems
File::Find::find({wanted => \&wanted}, '.');
exit;

sub wanted {
    my ($dev,$ino,$mode,$nlink,$uid,$gid);

    (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) &&
    -f _ &&                 # -type f
    /^.*l\z/s               # ^.*l$ regex to modify as you want
    && print("$name\n");    # -print
}

Now you have a base to start working around. You can modify the Perl regex as you want with your own regex.

Usage:

find2perl -type f -name '*l' > myPerlFind.pl
chmod +x myPerlFind.pl
$EDITOR myPerlFind.pl
./myPerlFind.pl

You can also use @ARGV to pass arguments to your script or even better: Getopt::Long

1

You can’t do it with find without rewriting find.

You requested no pipes, but it’s not possible without.

find . -print0 |
    perl -0ne 'print if /some perl regexp/s' | 
    xargs -r0 …

Where … is what you do with the files if just writing them to disk is insufficient.

Instead of perl, with GNU grep, you can also use grep -zP (or grep --null -P slightly more portable for greps of some BSDs that are based on the API of an older version of GNU grep).

A closer equivalent to a hypothetical find . -regextype perl -regex 'some perl regexp' -exec cmd {} +, with shells with support for ksh-style process substitution would be:

xargs -r0a <(
  find . -print0 |
    perl -0ne 'print if /some perl regexp/s') cmd

Which like -exec cmd {} + preserves cmd's stdin. Note that <(...) still uses pipes.

Note that by default, perl works byte-wise while find/grep tend to work character-wise. The -C or -Mopen=locale options can help decode bytes to characters before doing the matching.

Note that -print0, -r, -0, -a, -z, -P, -regex, -regex-type are all non-standard GNU extensions, with -print0/-0 now found in most other implementations. -r (to not run the command if no file is found) is also common. -regex (not -regex-type) as well though the default regex syntax varies with the implementation.

1

No, it is not. Or rather, yes it is possible, but you need to program it yourself. Unless you find an implementation of find that supports PCREs, and I personally don't know of one, your only choice is to code it yourself.

If a program you use doesn't have a feature you need, your only option is to ask the developers to implement it and hope they do, or to code it yourself. Since most of us don't have the necessary knowledge to go and extend the capabilities of a tool like find, I fear the answer to your question is basically "no".

0

FWIW, the globs of zsh, which have most of the features of find (and features not found in any find implementation) can do PCRE matching via the e glob qualifier:

set -o rematchpcre
print -rC1 -- **/*(NDe['[[ $REPLY =~ "some perl regexp" ]]'])

To print raw on 1 Column the files whose path matches the perl-like regular expression. Replace $REPLY with $REPLY:t to only match on the file's base name (tail) instead of full path.

-2

Something like this works (the regex matches not just the filename, but the full path):

find tmp/ -regex '.*pack.ge.jso.$'
1
  • 3
    While that's a valid Perl regex, it's also a valid POSIX basic (or extended) regex. It doesn't use any features specific to Perl regexes, so it hardly shows anything about them.
    – ilkkachu
    Commented Nov 30, 2022 at 21:03

You must log in to answer this question.

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