Чому іноді знахідка відповідає аргументу шляху аргументу командного рядка?


9

У Linux,

cd /tmp
mkdir foo; cd foo

Тепер біг

find . -name 'foo'

не дає виводу. В той час як біг

find /tmp/foo -name 'foo'

Дає вихід, /tmp/fooякий не має для мене сенсу. Хтось може пояснити, чому?


2
знаходити прорізи всередині даного шляху, включені як зазначено. Тож якщо ви введете, ./це не збігаєтьсяfoo
Костас

@Costas: Я це розумію. Я не розумію, чому це змінило значення, якщо я даю абсолютний шлях find.
Йорк

@York У певних ситуаціях немає поведінки, яка завжди є правильною. Уявіть символьне посилання з ім'ям, barяке вказує на файл, fooякий знаходиться за межами шляху пошуку. Чи відповідати це чи ні?
Hauke ​​Laging

1
The .і /tmp/fooне однакові - вони є двома різними жорсткими посиланнями на один і той же каталог; find /tmp/foo/. -name 'foo'теж нічого не знаходить.
jimmij

@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 .відповідає лише початковому каталогу ( .його не можна знайти під час рекурсивного обходу).


"він не містить жодного запису, окрім. та .., який не знаходить рекурсивного". Я припускаю, що "не поперечно рекурсивно" посилається на "..". Якщо це правильно, то що б "поперечна рекурсивно" означала в контексті ".."?
Faheem Mitha

@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- це як окреме (хоча спеціальне) ім'я файлу, а не символічне посилання або вираз, якому піддається розширення імені шляху оболонкою. Отже, перший тест, застосований знахідкою до аргументів командного рядка у наведеному прикладі, не відображатиме жодних збігів, оскільки .дійсно не відповідає шаблону імен, визначеному в -name 'foo'. Так само не відбувається, /tmp/foo/.оскільки тест для -nameшаблону виконується лише на базовому імені шляху (див. man find), Який тут знову є ..

Незважаючи на те, що така поведінка не може бути очікуваною або здаватися інтуїтивно зрозумілою з точки зору користувача (і так, спочатку мене це також збентежило), вона не є помилкою, але відповідає логіці та функціональності, описаним на сторінках man та info для ( гну) знайти.


0

Я намагався коментувати відповідь Гілла, але це було занадто довго, щоб вписатися в коментар. З цієї причини я ставлю це як відповідь на власне запитання.

Пояснення Джилла (та й Шевека) зрозуміле і має сенс. Ключовим моментом тут є те, що findнамагається не тільки узгодити назви файлів для файлів усередині заданих шляхів (рекурсивно), але й намагається відповідати базовій назві даних самих шляхів.

З іншого боку, чи є якісь докази того, що саме таким чином find, мабуть, працювати, а не бути помилкою? На мою думку, було б краще, якщо це не зробить результати суперечливими, find .і find ABSOLUTE-PATHтому, що непослідовність завжди заплутана і може витратити розробника багато часу, намагаючись зрозуміти, "що не так". У моєму випадку я писав сценарій, і шлях був взято зі змінної. Тож для того, щоб мій сценарій працював належним чином, те, що я можу думати про те, щоб зробити, це написати find $path/. -name 'pattern'.

Нарешті, я думаю, що досягнення послідовних результатів findможна досягти, завжди замінюючи .поточний каталог, перш ніж продовжувати.


-1

В fooкаталозі, з якого ви розпочали відносний пошук, не існує жодного об’єкта .

Ви вірно вважаєте, що gfindце баггі, коли він звітує, /tmp/fooколи абсолютне ім'я використовується як початкова директорія.

Gfindмає різні відхилення від стандартних, здається, ви знайшли ще одне. Коли вам подобається більш стандартне рішення, я рекомендую sfindце частина schilytools.

-nameзастосовується до результатів пошуку в каталозі. Жоден із двох згаданих вами випадків не поверне запис каталогу fooв readdir()операцію.


Я підозрюю, що це теж помилка. Ось вихід з find --version: find (GNU findutils) 4.4.2 Copyright (C) 2007 Free Free Foundation, Inc. Ліцензія GPLv3 +: GNU GPL версії 3 або пізнішої < gnu.org/licenses/gpl.html >
Йорк

Ну, я перевірив ваші приклади команд за допомогою find(сертифіковано POSIX), gfindі sfind; проблема існує лише тоді, коли ви користуєтесь gfind. В Linux gfindне встановлюється під рідним іменем, а під назвою find.
schily

3
Це правильно для find /tmp/foo -name fooвиводу /tmp/foo. (Cc @York) Це поведінка OpenBSD find, GNU find, BusyBox find, Solaris findта будь-яких інших сумісних з POSIX find("Первинний повинен оцінювати як істинне, якщо базове ім'я досліджуваного файлу відповідає шаблону (...)" - коли ім'я файлу це той, який передається як операнд контуру, жоден readdirвиклик не бере участь).
Жил "ТАК - перестань бути злим"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.