Is it possible to use regular expressions based on the result (file name) inside the exec
argument with find
? I want to be able to "exec" based on parts of the argument, like:
find . -name pattern -regex "foo (regex1) bar (Regex2)" -exec something $1 $2 ;
Best Answer
You can't use capture groups from the regexp in the command to execute. If you use
find -regex
to restrict matches, you'll have to do some extra matching in the command. You can do that by invoking a shell and using its own pattern matching constructs. For example, iffoo
andbar
are constant strings andregex1
can't matchbar
:Invoking a shell has a little overhead. You can improve performance a bit by invoking the shell in batches.
Since you've already done some filtering, you may be able to get away with shell patterns that match more than
regex1
andregex2
, but, for paths of that particular form, match the same part. Iffoo
andbar
can't be expressed with ordinary shell patterns, you can invoke ksh or bash, which support extra patterns that are as powerful as regular expressions:@(alter|native)
,*(zero-or-more)
,+(one-or-more)
,?(optional)
, and!(negated)
. In bash, these patterns need to be enabled withshopt -s extglob
. In ksh, they are available natively.In bash, there is a regular expression matching construct which you can use in conditionals:
[[ $STRING =~ REGEXP ]]
. The regexp is an ERE (likefind -regextype posix-egrep
). (Zsh has a similar one; ksh has=~
but doesn't expose capture groups.) Capture groups are available via theBASH_REMATCH
array.An alternative approach is to print out the result and filter it, then call
xargs
to invoke the program. Arrange to have the first and second argument as successive items and runxargs -n 2
. Use null bytes as separators to avoid xargs's strange quoting format, or use-d '\n'
to use strict line-by-line parsing. Recent GNU tools such as sed can work with null bytes instead of newlines to separate records.An alternative approach is to ditch find and use the recursive globbing feature of ksh93, bash or zsh:
**/
matches subdirectories recursively. This isn't possible for complex find expressions involving boolean connectors, but it's enough for most cases. For example, in bash (note that this recurses into symbolic links to directories, likefind -L
):In zsh: