найти поиск внутри заданного пути, как указано. Так что, если вы введете ./это не совпадаетfoo
Костас
@ Костас: я это понимаю. То, что я не понимаю, это то, почему это имело значение, если я даю абсолютный путь find.
Йорк,
@York В определенных ситуациях нет поведения, которое всегда правильно. Представьте себе символическую ссылку с именем, barкоторое указывает на файл, fooкоторый находится за пределами пути поиска. Это совпадение или нет?
Хауке Лагинг
1
Они .и /tmp/fooне одинаковы - это две разные жесткие ссылки на один и тот же каталог; find /tmp/foo/. -name 'foo'тоже ничего не находит.
Джимми
@jimmij: когда я запускался find /tmp/foo -name 'foo', я просил bash найти в каталоге /tmp/fooфайл с именем "foo". Поскольку каталог /tmp/fooпуст, он не должен был ничего возвращать. Я не понимаю, почему это возвращается /tmp/foo. С другой стороны, когда я запускался find . -name 'foo', я спрашивал bash то же самое, то есть, находил файл в текущем каталоге (который оказался /tmp/foo) с именем «foo», и он не возвращает ничего, что имеет смысл.
Йорк,
Ответы:
15
findОбходит указанное дерево каталогов и оценивает данное выражение для каждого найденного файла. Обход начинается с заданного пути. Вот краткое изложение того, как find . -name fooработает:
Первый путь в командной строке: .
Соответствует ли базовое имя ( .) шаблону foo? Нет, так ничего не делай.
Так получилось, что /tmp/fooэто другое имя для того же каталога. Но findне знает этого (и это не должно пытаться выяснить).
Является ли путь каталогом? Да, так пройти через это. Перечислите записи ., и для каждой записи выполните процесс обхода.
Каталог пуст: он не содержит никакой записи, кроме .и .., который findне проходит рекурсивно. Итак, работа закончена.
И find /tmp/foo:
Первый путь в командной строке: /tmp/foo
Соответствует ли базовое имя ( foo) шаблону foo? Да, так что условие соответствует.
С этим условием не связано никаких действий, поэтому выполните действие по умолчанию, которое заключается в печати пути.
Является ли путь каталогом? Да, так пройти через это. Перечислите записи /tmp/foo, и для каждой записи выполните процесс обхода.
Каталог пуст: он не содержит никакой записи, кроме .и .., который findне проходит рекурсивно. Итак, работа закончена.
Бывает так, что .и один /tmp/fooи тот же каталог, но этого недостаточно, чтобы гарантировать findодинаковое поведение на обоих. У findкоманды есть способы различать пути к одному и тому же файлу; -nameпредикат один из них. find /tmp/foo -name fooсоответствует начальному каталогу, а также любому файлу под ним, который называется foo. find . -name .соответствует только начальному каталогу ( .никогда не может быть найден во время рекурсивного обхода).
msgstr "он не содержит никаких записей, кроме. и .., которые находят не рекурсивно." Я предполагаю, что "не поперечно рекурсивно" относится к "..". Если это правильно, то что будет означать «рекурсивный поперечный» в контексте «..»?
Фахим Митха
@FaheemMitha «не проходит рекурсивно» относится как к, так .и к ... (При рекурсивном findобходе .он в конечном итоге будет проходить через каталог снова и снова, снова и снова и…) .и ..будет не просто специальным обозначением, а появлением в виде записей каталога.
Жиль "ТАК - перестань быть злым"
5
Перед применением тестов нормализация аргументов командной строки не производится. Таким образом, результаты различаются в зависимости от используемого пути (если задействованы символические ссылки):
cd /tmp
mkdir foo
ln -s foo bar
find /tmp/foo -name foo
find /tmp/bar -name foo
В «вашем случае» оба вызова дали бы один и тот же результат, который может быть (более) запутанным. Вы можете использовать, -mindepth 1если хотите, чтобы начальные точки игнорировались (может быть не POSIX).
-mindepth 1работает. Однако я до сих пор не понимаю, почему find . -name 'foo'и веду find /tmp/foo -name 'foo'себя по-другому.
Йорк,
2
(gnu) find показывает любые совпадения, найденные в пути, указанном в команде, потому что он начинает свое сравнение с аргументами командной строки, оттуда спускаясь глубже в структуру каталогов (таким образом, -maxdepth 0тесты ограничиваются базовым уровнем или только аргументами командной строки, тогда как -mindepth 1пропускает аргументы командной строки, как man findобъясняет). По этой причине find /tmp/foo -name 'foo'будет получено одно совпадение, даже если сам каталог пуст.
find . -name 'foo'с другой стороны, не даст никакого результата, потому что .(точка) - это специальный файл, который действует как жесткая ссылка на тот же индекс, что и /tmp/foo- он похож на отдельное (хотя и специальное) имя файла, а не на символическую ссылку или выражение, которое подвергается расширение пути с помощью оболочки. Следовательно, первый тест, примененный командой find к аргументам командной строки в данном примере, не будет показывать никаких совпадений, поскольку в .действительности не соответствует шаблону имени, определенному в -name 'foo'. Это не /tmp/foo/.так, поскольку проверка -nameшаблона выполняется только для базового имени пути (см. man find), Которое также здесь ..
Хотя это поведение может не ожидаться или казаться интуитивно понятным с точки зрения пользователя (и да, поначалу меня это также смутило), оно не представляет собой ошибку, но соответствует логике и функциональности, описанным на страницах man и info для ( гну) найди.
Я пытался комментировать ответ Жиля, но это было слишком долго, чтобы уместиться в комментарии. По этой причине я ставлю это как ответ на свой вопрос.
Объяснения Жиля (и Шевека тоже) ясны и имеют смысл. Ключевым моментом здесь является то, что он не только findпытается сопоставить имена файлов для файлов внутри заданных путей (рекурсивно), но также пытается сопоставить базовое имя самих заданных путей.
С другой стороны, есть ли доказательства того, что именно так findи должно работать, а не быть ошибкой? На мой взгляд, было бы лучше, если бы это не делало результаты непоследовательными, find .и find ABSOLUTE-PATHпотому что непоследовательность всегда сбивает с толку и может потратить много времени разработчика, пытаясь выяснить, «что было не так». В моем случае я писал скрипт, а путь был взят из переменной. Поэтому, чтобы мой сценарий работал должным образом, мне нужно написать find $path/. -name 'pattern'.
Наконец, я думаю, что получение согласованных результатов findможет быть достигнуто, всегда заменяя .текущий каталог перед продолжением.
В fooкаталоге, в котором вы начали относительный поиск, нет названного объекта .
Вы правы, предполагая, что gfindэто ошибка, когда он сообщает об /tmp/fooиспользовании абсолютного имени в качестве начального каталога.
Gfindимеет различные отклонения от стандарта, кажется, вы нашли другое. Если вам нравится более стандартное совместимое решение, я рекомендую sfindего использовать schilytools.
-nameотносится к результатам поиска в каталоге. Ни один из двух упомянутых вами случаев не вернет запись каталога fooиз readdir()операции.
Я подозреваю, что это тоже ошибка. Вот вывод из find --version: find (GNU findutils) 4.4.2 Copyright (C) 2007 Free Software Foundation, Inc. Лицензия GPLv3 +: GNU GPL версии 3 или более поздней < gnu.org/licenses/gpl.html >
Йорк,
Ну, я проверил ваши примеры команд с find(сертифицирован POSIX) gfindи sfind; проблема существует только при использовании gfind. В Linux gfindне устанавливается под собственным именем, а под именем find.
Щили
3
Это правильно для find /tmp/foo -name fooвывода /tmp/foo. (Cc @York) Это поведение OpenBSD find, GNU find, BusyBox find, Solaris findи любого другого POSIX-совместимого find(«Первичный должен оцениваться как true, если базовое имя проверяемого имени файла соответствует шаблону (…)» - когда имя файла это тот, который передается как операнд пути, readdirвызов не задействован).
./
это не совпадаетfoo
find
.bar
которое указывает на файл,foo
который находится за пределами пути поиска. Это совпадение или нет?.
и/tmp/foo
не одинаковы - это две разные жесткие ссылки на один и тот же каталог;find /tmp/foo/. -name 'foo'
тоже ничего не находит.find /tmp/foo -name 'foo'
, я просил bash найти в каталоге/tmp/foo
файл с именем "foo". Поскольку каталог/tmp/foo
пуст, он не должен был ничего возвращать. Я не понимаю, почему это возвращается/tmp/foo
. С другой стороны, когда я запускалсяfind . -name 'foo'
, я спрашивал bash то же самое, то есть, находил файл в текущем каталоге (который оказался/tmp/foo
) с именем «foo», и он не возвращает ничего, что имеет смысл.Ответы:
find
Обходит указанное дерево каталогов и оценивает данное выражение для каждого найденного файла. Обход начинается с заданного пути. Вот краткое изложение того, какfind . -name foo
работает:.
.
) шаблонуfoo
? Нет, так ничего не делай.Так получилось, что
/tmp/foo
это другое имя для того же каталога. Ноfind
не знает этого (и это не должно пытаться выяснить)..
, и для каждой записи выполните процесс обхода..
и..
, которыйfind
не проходит рекурсивно. Итак, работа закончена.И
find /tmp/foo
:/tmp/foo
foo
) шаблонуfoo
? Да, так что условие соответствует./tmp/foo
, и для каждой записи выполните процесс обхода..
и..
, которыйfind
не проходит рекурсивно. Итак, работа закончена.Бывает так, что
.
и один/tmp/foo
и тот же каталог, но этого недостаточно, чтобы гарантироватьfind
одинаковое поведение на обоих. Уfind
команды есть способы различать пути к одному и тому же файлу;-name
предикат один из них.find /tmp/foo -name foo
соответствует начальному каталогу, а также любому файлу под ним, который называетсяfoo
.find . -name .
соответствует только начальному каталогу (.
никогда не может быть найден во время рекурсивного обхода).источник
.
и к..
. (При рекурсивномfind
обходе.
он в конечном итоге будет проходить через каталог снова и снова, снова и снова и…).
и..
будет не просто специальным обозначением, а появлением в виде записей каталога.Перед применением тестов нормализация аргументов командной строки не производится. Таким образом, результаты различаются в зависимости от используемого пути (если задействованы символические ссылки):
В «вашем случае» оба вызова дали бы один и тот же результат, который может быть (более) запутанным. Вы можете использовать,
-mindepth 1
если хотите, чтобы начальные точки игнорировались (может быть не POSIX).источник
-mindepth 1
работает. Однако я до сих пор не понимаю, почемуfind . -name 'foo'
и ведуfind /tmp/foo -name 'foo'
себя по-другому.(gnu) find показывает любые совпадения, найденные в пути, указанном в команде, потому что он начинает свое сравнение с аргументами командной строки, оттуда спускаясь глубже в структуру каталогов (таким образом,
-maxdepth 0
тесты ограничиваются базовым уровнем или только аргументами командной строки, тогда как-mindepth 1
пропускает аргументы командной строки, какman find
объясняет). По этой причинеfind /tmp/foo -name 'foo'
будет получено одно совпадение, даже если сам каталог пуст.find . -name 'foo'
с другой стороны, не даст никакого результата, потому что.
(точка) - это специальный файл, который действует как жесткая ссылка на тот же индекс, что и/tmp/foo
- он похож на отдельное (хотя и специальное) имя файла, а не на символическую ссылку или выражение, которое подвергается расширение пути с помощью оболочки. Следовательно, первый тест, примененный командой find к аргументам командной строки в данном примере, не будет показывать никаких совпадений, поскольку в.
действительности не соответствует шаблону имени, определенному в-name 'foo'
. Это не/tmp/foo/.
так, поскольку проверка-name
шаблона выполняется только для базового имени пути (см.man find
), Которое также здесь.
.Хотя это поведение может не ожидаться или казаться интуитивно понятным с точки зрения пользователя (и да, поначалу меня это также смутило), оно не представляет собой ошибку, но соответствует логике и функциональности, описанным на страницах man и info для ( гну) найди.
источник
Я пытался комментировать ответ Жиля, но это было слишком долго, чтобы уместиться в комментарии. По этой причине я ставлю это как ответ на свой вопрос.
Объяснения Жиля (и Шевека тоже) ясны и имеют смысл. Ключевым моментом здесь является то, что он не только
find
пытается сопоставить имена файлов для файлов внутри заданных путей (рекурсивно), но также пытается сопоставить базовое имя самих заданных путей.С другой стороны, есть ли доказательства того, что именно так
find
и должно работать, а не быть ошибкой? На мой взгляд, было бы лучше, если бы это не делало результаты непоследовательными,find .
иfind ABSOLUTE-PATH
потому что непоследовательность всегда сбивает с толку и может потратить много времени разработчика, пытаясь выяснить, «что было не так». В моем случае я писал скрипт, а путь был взят из переменной. Поэтому, чтобы мой сценарий работал должным образом, мне нужно написатьfind $path/. -name 'pattern'
.Наконец, я думаю, что получение согласованных результатов
find
может быть достигнуто, всегда заменяя.
текущий каталог перед продолжением.источник
В
foo
каталоге, в котором вы начали относительный поиск, нет названного объекта .Вы правы, предполагая, что
gfind
это ошибка, когда он сообщает об/tmp/foo
использовании абсолютного имени в качестве начального каталога.Gfind
имеет различные отклонения от стандарта, кажется, вы нашли другое. Если вам нравится более стандартное совместимое решение, я рекомендуюsfind
его использоватьschilytools
.-name
относится к результатам поиска в каталоге. Ни один из двух упомянутых вами случаев не вернет запись каталогаfoo
изreaddir()
операции.источник
find --version
: find (GNU findutils) 4.4.2 Copyright (C) 2007 Free Software Foundation, Inc. Лицензия GPLv3 +: GNU GPL версии 3 или более поздней < gnu.org/licenses/gpl.html >find
(сертифицирован POSIX)gfind
иsfind
; проблема существует только при использованииgfind
. В Linuxgfind
не устанавливается под собственным именем, а под именемfind
.find /tmp/foo -name foo
вывода/tmp/foo
. (Cc @York) Это поведение OpenBSDfind
, GNUfind
, BusyBoxfind
, Solarisfind
и любого другого POSIX-совместимогоfind
(«Первичный должен оцениваться как true, если базовое имя проверяемого имени файла соответствует шаблону (…)» - когда имя файла это тот, который передается как операнд пути,readdir
вызов не задействован).