1

I'd like to use find to match a path but I found this:

-path pattern

File name matches shell pattern pattern. The metacharacters do not treat /' or.' specially; so, for example,

  find . -path "./sr*sc"

will print an entry for a directory called ./src/misc (if one exists).

How can I make it only match files in the current dir? Do I need another tool for this job?

Edit. As suggested here's some details. To do this job there are -maxdepth and -regex for use. However, in real applications the path may be a variable instead of a simple constant here, where -maxdepth won't work well and -regex may look like an overkill. So I'd like to know if there is a simple solution with find to make it respect slash. Below is rsync doc describing its behavior which is very close to what I want.

Currently best solution is using regex, but if you know anything that can make it simpler that'd be welcome.


As an example of what I want, here is rsync pattern rule:

  • a ’*’ matches any path component, but it stops at slashes.

  • use ’**’ to match anything, including slashes.

2

Use -regex instead. The pattern is anchored at start and end so you will need to use .* at the start and end probably. Use [^/]*/ to replace a glob * that does not match /. Eg:

-regex '.*/sr[^/]*sc.*'

There is a small Perl script find2perl that claims to convert the arguments to a find command into an equivalent Perl script. Obviously, it will have the same pattern matching for -path as find, but you can edit it to accept another argument, say -glob, which would then use the system call fnmatch() to do the sort of globbing you want. I did a quick modification to my version of find2perl as shown in this patch:

--- /usr/bin/find2perl  2016-02-04 19:28:48.000000000 +0100
+++ /tmp/find2perl      2018-07-31 20:38:10.034175198 +0200
@@ -58,2 +58,4 @@
     $out .= tab . '$File::Find::name =~ /' . fileglob_to_re(shift) . "/s$1";
+    } elsif (/^glob$/) {
+        $out .= tab . 'fnmatch("'.(shift).'", $name, FNM_PATHNAME|FNM_PERIOD)';
 } elsif ($_ eq 'perm') {
@@ -236,2 +238,3 @@
 use File::Find ();
+use File::FnMatch qw(:fnmatch);    # import everything

(The lines beginning + show the 3 lines I added). I needed to add packages perl-App-find2perl and perl-File-FnMatch for Fedora. You would need to find the equivalent on your system.

You can then run commands like

/tmp/find2perl . -glob './sr*sc' | perl
  • This is probably the only thing I can find right now. So using regex for such a simple task is not an overkill? – Cyker Jul 31 '18 at 16:43
  • I added a Perl alternative that at least means you can use the glob syntax you want, instead of having to convert it to a regex. Perl usually runs very close to C speeds. – meuh Jul 31 '18 at 18:55
1

To prevent find from recursing into subdirectories, use -maxdepth flag

find -maxdepth 1 -name "sr*sc"
  • True but if the path variable changes you have to take care of the depth variable. And if the path variable comes from elsewhere you don't really know the depth variable. – Cyker Jul 31 '18 at 16:37
  • 3
    @Cyker What variable? You never mentioned a variable, you only said "the current directory". – Kusalananda Jul 31 '18 at 16:49
  • @Kusalananda This is only a simple example pasted from man page. In more complicated situations the path may be a variable rather than a constant ./sr*rc. The point is I'd like to know if find can treat slash specially so I don't want to make the example very complex. – Cyker Jul 31 '18 at 16:54
  • 2
    @Cyker The question explicitly asks "How can I make it only match files in the current dir?" You need to update this and be clear about what you actually mean, preferably with examples. – Kusalananda Jul 31 '18 at 16:57
  • @Kusalananda It's in the title but I added more details in question to make it even more clear. – Cyker Jul 31 '18 at 17:03
1

Some shells, such as zsh, bash and ksh93 supports the ** filename globbing pattern. In zsh it is enabled by default while bash has to have it enabled with shopt -s globstar and ksh93 with set -o globstar.

This means you should be able to do, for example,

for pathname in ./src/**/misc/*.c
    # process "$pathname" here
done

A few find-like tests, such as -type f, could easily be incorporated into this:

for pathname in ./src/**/misc/*.c
    [ -f "$pathname" ] || continue  # will not skip symbolic links to regular files though
    # process "$pathname" here
done

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.