Skip to main content
fix non-portable regex syntax (thanks Stéphane Chazelas); exclude symbolic links to regular files in sh
Source Link
Gilles 'SO- stop being evil'
  • 865.3k
  • 205
  • 1.8k
  • 2.3k

** has no special meaning in find patterns. -path '*/foo/*' would find all files under a directory called foo, including files in subdirectories. -path '*/foo/*' ! -path '*/foo/*/*' would exclude files like a/foo/b/foo/c. I don't think you can do this with just one invocation of POSIX find.

With find implementations that support -regex (GNU, BusyBox, FreeBSD, NetBSD), you can use that to ensure that there's a single / after foo.

find . -regex '.*/foo/[^/]+']*' -type f

Alternatively, you can use find to locate the foo directories and a shell to enumerate files in this directory.

Another potential approach would be to invoke find again, but I can't find a pure POSIX find solution that actually works. With any POSIX find, invoked again for each foo directory:

find . -name foo -type d -exec find {} -type d ! -name foo -prune -o -type f \;

Beware that this mostly works, but not quite, and it's a little fragile. You need -type d -prune to avoid recursing into subdirectories, but with just -type d -prune, find would stop at the foo directory. ! -name foo does not prune foo/foo, so a file like foo/foo/bar will be reported twice. You can't use -exec in the inner find because its {} would be interpreted by the outer find. If your find has -maxdepth (which is being considered for inclusion in the next version of POSIX), you can make this reliable, but there's still this limitation against -exec:

find . -name foo -type d -exec find {} -maxdepth 1 -type f \;

With any POSIX find and sh:

find . -name foo -type d -exec sh -c 'for'
    for x in "$0/"* "$0/".*; do
      if [ -f "$x" ] && ! [ -L "$x" ]; then
        …;
      fi; 
 done'   done
' {} \;

Substitute the code you want to run on the file names for .

** has no special meaning in find patterns. -path '*/foo/*' would find all files under a directory called foo, including files in subdirectories. -path '*/foo/*' ! -path '*/foo/*/*' would exclude files like a/foo/b/foo/c. I don't think you can do this with just one invocation of POSIX find.

With find implementations that support -regex (GNU, BusyBox, FreeBSD, NetBSD), you can use that to ensure that there's a single / after foo.

find . -regex '.*/foo/[^/]+' -type f

Alternatively, you can use find to locate the foo directories and a shell to enumerate files in this directory.

Another potential approach would be to invoke find again, but I can't find a pure POSIX find solution that actually works. With any POSIX find, invoked again for each foo directory:

find . -name foo -type d -exec find {} -type d ! -name foo -prune -o -type f \;

Beware that this mostly works, but not quite, and it's a little fragile. You need -type d -prune to avoid recursing into subdirectories, but with just -type d -prune, find would stop at the foo directory. ! -name foo does not prune foo/foo, so a file like foo/foo/bar will be reported twice. You can't use -exec in the inner find because its {} would be interpreted by the outer find. If your find has -maxdepth (which is being considered for inclusion in the next version of POSIX), you can make this reliable, but there's still this limitation against -exec:

find . -name foo -type d -exec find {} -maxdepth 1 -type f \;

With any POSIX find and sh:

find . -name foo -type d -exec sh -c 'for x in "$0/"* "$0/".*; do if [ -f "$x" ]; then …; fi; done' {} \;

Substitute the code you want to run on the file names for .

** has no special meaning in find patterns. -path '*/foo/*' would find all files under a directory called foo, including files in subdirectories. -path '*/foo/*' ! -path '*/foo/*/*' would exclude files like a/foo/b/foo/c. I don't think you can do this with just one invocation of POSIX find.

With find implementations that support -regex (GNU, BusyBox, FreeBSD, NetBSD), you can use that to ensure that there's a single / after foo.

find . -regex '.*/foo/[^/]*' -type f

Alternatively, you can use find to locate the foo directories and a shell to enumerate files in this directory.

Another potential approach would be to invoke find again, but I can't find a pure POSIX find solution that actually works. With any POSIX find, invoked again for each foo directory:

find . -name foo -type d -exec find {} -type d ! -name foo -prune -o -type f \;

Beware that this mostly works, but not quite, and it's a little fragile. You need -type d -prune to avoid recursing into subdirectories, but with just -type d -prune, find would stop at the foo directory. ! -name foo does not prune foo/foo, so a file like foo/foo/bar will be reported twice. You can't use -exec in the inner find because its {} would be interpreted by the outer find. If your find has -maxdepth (which is being considered for inclusion in the next version of POSIX), you can make this reliable, but there's still this limitation against -exec:

find . -name foo -type d -exec find {} -maxdepth 1 -type f \;

With any POSIX find and sh:

find . -name foo -type d -exec sh -c '
    for x in "$0/"* "$0/".*; do
      if [ -f "$x" ] && ! [ -L "$x" ]; then
        …;
      fi; 
    done
' {} \;

Substitute the code you want to run on the file names for .

Source Link
Gilles 'SO- stop being evil'
  • 865.3k
  • 205
  • 1.8k
  • 2.3k

** has no special meaning in find patterns. -path '*/foo/*' would find all files under a directory called foo, including files in subdirectories. -path '*/foo/*' ! -path '*/foo/*/*' would exclude files like a/foo/b/foo/c. I don't think you can do this with just one invocation of POSIX find.

With find implementations that support -regex (GNU, BusyBox, FreeBSD, NetBSD), you can use that to ensure that there's a single / after foo.

find . -regex '.*/foo/[^/]+' -type f

Alternatively, you can use find to locate the foo directories and a shell to enumerate files in this directory.

Another potential approach would be to invoke find again, but I can't find a pure POSIX find solution that actually works. With any POSIX find, invoked again for each foo directory:

find . -name foo -type d -exec find {} -type d ! -name foo -prune -o -type f \;

Beware that this mostly works, but not quite, and it's a little fragile. You need -type d -prune to avoid recursing into subdirectories, but with just -type d -prune, find would stop at the foo directory. ! -name foo does not prune foo/foo, so a file like foo/foo/bar will be reported twice. You can't use -exec in the inner find because its {} would be interpreted by the outer find. If your find has -maxdepth (which is being considered for inclusion in the next version of POSIX), you can make this reliable, but there's still this limitation against -exec:

find . -name foo -type d -exec find {} -maxdepth 1 -type f \;

With any POSIX find and sh:

find . -name foo -type d -exec sh -c 'for x in "$0/"* "$0/".*; do if [ -f "$x" ]; then …; fi; done' {} \;

Substitute the code you want to run on the file names for .