Остановить рекурсию команды 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:-.}
принять первый аргумент в качестве корневого каталога или .
если не прошло