user3142695
user3142695

Reputation: 17362

Find file recursively excluding subfolders with specific substring

I need to get all paths which has a specific file:

find apps  -type f -name "project.json"

This returns something like

apps/sub/frontend-e2e/project.json
apps/sub/frontend/project.json
apps/sub/backend/project.json

But I want to exclude all paths with -e2e in the last folder.

I tried something like

find apps  -type f -name "project.json" -not \( -path "*-e2e" -prune \)

Also I need to remove apps/ and /project.json from the beginning and the end of each path. So the result should be:

sub/frontend
sub/backend

In JS I would do

glob.sync('apps/**/project.json', {
    ignore: ['apps/**/*-e2e/project.json']
}).map((path) => {
    // do replacements
})

Upvotes: 3

Views: 66

Answers (2)

pjh
pjh

Reputation: 8209

If you've got Bash 4.3 (released in 2014) or later, try this Shellcheck-clean code:

shopt -s dotglob extglob globstar nullglob
for path in apps/**/!(*-e2e)/project.json; do
    p=${path#*/}
    printf '%s\n' "${p%/*}"
done
  • shopt -s ... enables some Bash settings that are required by the code:
    • dotglob enables globs to match files and directories that begin with .. find shows such files by default.
    • extglob enables "extended globbing" (including patterns like !(*-e2e)). See the extglob section in glob - Greg's Wiki.
    • globstar enables the use of ** to match paths recursively through directory trees. This option was introduced with Bash 4.0 but it is dangerous to use in versions before 4.3 because it follows symlinks.
    • nullglob makes globs expand to nothing when nothing matches (otherwise they expand to the glob pattern itself, which is almost never useful in programs).
  • See Removing part of a string (BashFAQ/100 (How do I do string manipulation in bash?)) for an explanation of ${path#*/} and ${p%/*}.
  • See the accepted, and excellent, answer to Why is printf better than echo? for an explanation of why I used printf instead of echo to print the outputs.

Note that this code will not output anything for the path apps/project.json. It's not clear what you would want to output in that case anyway.

Upvotes: 1

Gilles Quénot
Gilles Quénot

Reputation: 185851

Like this:

$ tree apps/
apps/
└── sub
    ├── backend
    │   └── project.json
    ├── frontend
    │   └── project.json
    └── frontend-e2e
        └── project.json

4 directories, 3 files

$ find ./apps -not -path '*-e2e/project.json' -name project.json -printf '%h\n'
apps/sub/frontend
apps/sub/backend

Upvotes: 3

Related Questions