Почему моя команда `find` выдаёт мне ошибки, связанные с игнорируемыми каталогами?
У меня есть эта команда поиска:
find . -type f -not -path '**/.git/**' -not -path '**/node_modules/**' | xargs sed -i '' s/typescript-library-skeleton/xxx/g;
по некоторым причинам это дает мне эти предупреждения / ошибки:
find: ./.git/objects/3c: No such file or directory
find: ./.git/objects/3f: No such file or directory
find: ./.git/objects/41: No such file or directory
Я даже пытался использовать:
-not -path '**/.git/objects/**'
и получил то же самое. Кто-нибудь знает, почему поиск ищет в .git
каталог? Кажется странным
2 ответа
почему поиск находит в каталоге.git?
GNU find
является умным и поддерживает несколько оптимизаций по сравнению с наивной реализацией:
- Это может перевернуть порядок
-size +512b -name '*.txt'
и сначала проверьте имя, потому что запрос размера потребует второго системного вызова. - Он может сосчитать жесткие ссылки каталога, чтобы определить количество подкаталогов, и когда он все видит, ему больше не нужно проверять их
-type d
или для повторения. - Это может даже переписать
(-B -or -C) -and -A
так что если проверки одинаково дороги и не имеют побочных эффектов,-A
будет оцениваться первым, надеясь отклонить файл после 1 теста вместо 2.
Тем не менее, это еще не достаточно умно, чтобы понять, что -not -path '*/.git/*'
означает, что если вы найдете каталог .git
тогда вам даже не нужно возвращаться в него, потому что все файлы внутри не будут совпадать.
Вместо этого он покорно повторяет, находит каждый файл и сопоставляет его с шаблоном, как если бы это был черный ящик.
Чтобы явно сказать ему пропустить каталог полностью, вы можете вместо этого использовать -prune
, См. Как исключить каталог из поиска. команда
И более эффективным, и более правильным было бы избежать дефолта -print
действие, изменение -not -path ...
в -prune
и убедитесь, что xargs
используется только с вводом, разделенным NUL:
find . -name .git -prune -o \
-name node_modules -prune -o \
-type f -print0 | xargs -0 sed -i '' s/typescript-library-skeleton/xxx/g '{}' +
Обратите внимание на следующие моменты:
- Мы используем
-prune
сказатьfind
даже не отбрасывать нежелательные каталоги, а не-not -path ...
сказать ему, чтобы отменить имена в этих каталогах после того, как они были найдены. - Мы ставим
-prune
с до-type f
Таким образом, мы можем сопоставить каталоги для обрезки. - У нас есть явное действие, не зависящее от значения по умолчанию
-print
, Это важно, потому что по умолчанию-print
эффективно имеет набор скобок:find ...
ведет себя какfind '(' ... ')' -print
, не какfind ... -print
, нет, если задано явное действие. - Мы используем
xargs
только с-0
аргумент, разрешающий ввод с разделением NUL, и-print0
действие наfind
сторона для создания списка имен, разделенных NUL. NUL - это единственный символ, который не может присутствовать в произвольном пути к файлу (да, могут присутствовать символы новой строки) - и, таким образом, единственный символ, который безопасно использовать для разделения путей. (Если-0
расширение доxargs
и-print0
расширение доfind
не гарантируется быть доступным, используйте-exec sed -i '' ... {} +
вместо).