Остановить рекурсию команды find, если совпадение найдено

Используя GNU findutils, мне нужно найти дерево каталогов для определенного файла. Если файл был найден для данной ветки, я хочу предотвратить повторный поиск find в ветке. Скажем, я хочу найти файл foo, и это мое дерево каталогов:

├── a
│   ├── a1
│   │   └── foo
│   └── foo
├── b
└── c
    └── foo

Поскольку я ищу дерево выше, я хочу найти / foo и c / foo. Однако я не хочу находить /a1/foo, так как я уже нашел foo в родительском каталоге для a1. Кажется, я должен использовать флаг -prune для команды поиска, и я нашел эту ссылку https://unix.stackexchange.com/questions/24557/how-do-i-stop-a-find-from-descending-into-found-directories например, находят каталоги, но я не могу заставить его работать. Мои попытки включают в себя:

$ find -name foo -type f -prune
./a/a1/foo <- Unwanted hit
./a/foo
./c/foo

а также

$ find -name foo -type f -prune -exec find ../foo -type f {} \;
find: paths must precede expression: ./a/a1/foo
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]
find: paths must precede expression: ./a/foo
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]
find: paths must precede expression: ./c/foo
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]

2 ответа

Решение

Это напечатает каталоги, которые содержат foo, и не буду возвращаться в свои подкаталоги:

find -type d -exec test -f {}/foo \; -print -prune

Поведение для {}/foo явно не определено POSIX:

Если имя_программы или строка аргумента содержит два символа "{}", а не только два символа "{}", то определяется реализацией, заменяет ли find эти два символа или использует строку без изменений.

но работает как положено с GNU find (и вы пометили вопрос с помощью gnu-findutils). Как справедливо предполагает Камил Цук в комментариях, если вы используете не-GNU find или если вы хотите более портативное решение, используйте:

find -type d -exec sh -c 'test -f "$1"/foo' -- {} \; -print -prune

Это не легко сделать с помощью find -prune потому что он работает с каталогами и найти основные условия по текущему файлу.

альтернативой может быть сделать это с помощью bash программно, используя рекурсивную функцию, в основном

rec_run() {
    local file
    for file in "${1:-.}"/*; do
        # to filter 'file=*' when no match is found
        if [[ ! -e $file ]]; then
            continue
        fi

        # do something with file
        echo "$file"

        # to filter known hard links
        if [[ $file = */. ]] || [[ $file = */.. ]]; then
            continue
        fi

        # if is a directory recursive call
        if [[ -d $file ]]; then
            rec_run "$file";
        fi
    done
}

и изменение части # do something с

    if [[ -f $file/foo ]]; then
        echo "$file/foo"
        continue
    fi

Вот foo жестко закодирован, но может быть передан в качестве второго аргумента функции

Заметка ${1:-.} принять первый аргумент в качестве корневого каталога или . если не прошло

Другие вопросы по тегам