GNU Find – Understanding -regex with GNU Find

findregular expressionxargs

Background

I have what I think should be a simple case. I want to find all files with "cisco" in the name and do something with those files (via xargs).

Finding files with ls

Before I use xargs, the first step is listing all relevant files. Listing files is easy with ls | grep cisco

[mpenning@Bucksnort post]$ ls | grep cisco
cisco-asa-double-nat.rst
cisco-asa-packet-capture.rst
cisco-eem-tcl.rst
cisco-ip-sla-tracking.rst
cisco_autonomous_to_lwap.rst
[mpenning@Bucksnort post]$

Finding files with find

Although it's probably not required in this specific case, find is generally considered safer when piping into xargs. However, all logic seems to go out the window when I use find -regex.

[mpenning@Bucksnort post]$ find -regextype grep -regex ".*/cisco*" -print
[mpenning@Bucksnort post]$

However, I know I can find those files…

[mpenning@Bucksnort post]$ find | grep cisco
./cisco-eem-tcl.rst
./parsing-cisco-output-w-textfsm.rst
./cisco_autonomous_to_lwap.rst
./cisco-ip-sla-tracking.rst
./cisco-asa-double-nat.rst
./cisco-asa-packet-capture.rst
[mpenning@Bucksnort post]$

Questions

I realize that find -regex has to match the full path that is returned, but why isn't find -regextype grep -regex ".*/cisco*" -print working above? Shouldn't .*/cisco* match the path?


NOTES

I know I could just use find -path "*cisco*" to solve the problem, but the point of the question is to understand why my -regex usage is wrong.

Best Answer

Finding with ls: first things first, ls | grep cisco is a bit verbose, since cisco isn't a regular expression. Try:

ls *cisco*

Using find: along the same lines, -regex is overkill with a simple, static pattern. How about:

find -name '*cisco*'

The quotes are required so the glob is interpreted by find, not the shell. Also, -print is required for many versions of find, but is optional (and the default predicate) for others (e.g. GNU find). Feel free to add it if you need it.

If you need to search for ‘cisco’ in the full pathname, you could try this:

find -path '*cisco*'

which is equivalent to find | fgrep cisco.

Using find with regular expressions: let's do that anyway, since this is what you want. Shamelessly copying from the GNU find manpage:

-regex pattern

          File  name  matches  regular  expression  pattern.  This is a match
          on the whole path, not a search.  For example, to match a file named
          `./fubar3', you can use the regular expression `.*bar.' or `.*b.*3',
          but not `f.*r3'.

What this means is that your regular expression is wrapped in an invisible ^...$, so it must match every character in the full pathname of the file. So, as nwildner and otokan said in the comments, you should use something like:

find -regex '.*cisco.*'

And you don't even need the -regextype for something this simple.

Related Question