Почему моя команда `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 '' ... {} + вместо).
Другие вопросы по тегам