Чому команда «знайти | grep "ім'я файлу" "настільки повільніше, ніж" знайти "ім'я файлу"?


10

Я спробував обидві команди, і команда find | grep 'filename' в багато разів повільніше, ніж проста find 'filename' команда.

Що було б правильним поясненням такої поведінки?


2
Ви перераховуєте кожен файл із знахідкою, а потім передаєте дані у файл grep для обробки. При пошуку, який використовується самостійно, вам не вистачає кроку проходження кожного перерахованого файлу в grep для розбору результатів. Тому це буде швидше.
Raman Sailopal

Повільніше в якому сенсі? Чи потрібно командам займати іншу кількість часу?
Кусалаланда

1
Я не можу відтворити це локально. Якщо що, time find "$HOME" -name '.profile'звітує довше, ніж time find "$HOME" | grep -F '.profile'. (17s проти 12s).
Kusalananda

2
@JenniferAnderson Я бігав обох неодноразово. 17 і 12 секунд - це середні показники. І так, grepваріація буде відповідати будь-якій точці findрезультату, тоді як відповідність з find -nameлише відповідатиме точно (у цьому випадку).
Кусалаланда

2
Так, find filename було б швидко . Я якось припускав, що це помилка друку і що ОП означає find -name filename. З find filename, тільки filenameбуло б обстежено (і більше нічого).
Kusalananda

Відповіді:


11

(Я припускаю GNU findтут)

Використовуючи просто

find filename

було б швидко, тому що воно просто повернеться filename, або імена всередині, filenameякщо це каталог, або помилка, якщо це ім'я не існувало в поточному каталозі. Це дуже швидка операція, подібна до ls filename(але рекурсивна, якщо filenameце каталог).

У контрасті,

find | grep filename

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

Я припускаю, що насправді було призначено

find . -type f -name 'filename'

Це буде шукати filenameяк ім'я звичайного файлу в будь-якій точці поточного каталогу чи нижче.

Це буде настільки ж швидко (або порівняно швидко) find | grep filename, але grepрішення відповідатиме filenameповним шляхом кожного знайденого імені, аналогічно тому, що -path '*filename*'і з цим find.


Плутанина виникає через нерозуміння того, як findпрацює.

Утиліта приймає ряд шляхів і повертає всі імена під цими стежками.

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

Коли ти кажеш

find a b c

ви попросите findперелічити кожне ім’я, доступне під трьома шляхами a, bі c. Якщо це будуть імена звичайних файлів у поточному каталозі, вони будуть повернуті. Якщо будь-який з них буде іменем каталогу, він буде повернутий разом з усіма подальшими іменами всередині цього каталогу.

Коли я це роблю

find . -type f -name 'filename'

Це створює список усіх імен у поточному каталозі ( .) та нижче. Тоді він обмежує імена тими звичайними файлами, тобто не каталогами тощо -type f. Потім є додаткове обмеження на імена, які відповідають filenameвикористанню -name 'filename'. Рядок filenameможе бути шаблоном імені файлу, таким як *.txt(просто пам’ятайте, щоб його цитувати!).

Приклад:

Здається, що "знайти" файл, викликаний .profileу моєму домашньому каталозі:

$ pwd
/home/kk
$ find .profile
.profile

Але насправді він просто повертає всі імена на шляху .profile(є лише одне ім’я, і це є у цьому файлі).

Потім я cdпіднімаю один рівень і повторюю спробу:

$ cd ..
$ pwd
/home
$ find .profile
find: .profile: No such file or directory

Тепер findкоманда не може знайти жодного названого шляху .profile.

Однак, якщо я змусив його переглянути поточний каталог, а потім обмежити лише повернені імена.profile , він знайде його і звідти:

$ pwd
/home
$ find . -name '.profile'
./kk/.profile

1
find filenameповернеться лише в тому filenameвипадку, якщо б filenameне було каталогу типів (або було в каталозі типу, але не було жодного запису)
Stéphane Chazelas

2

Нетехнічне пояснення: Шукати Джека в натовпі швидше, ніж шукати всіх у натовпі та виключати всіх із розгляду, крім Джека.


Проблема полягає в тому, що ОП очікує, що Джек буде єдиною людиною в натовпі. Якщо це так, їм пощастило. find jackперелічить, jackякщо це файл, який називається jack, або всі імена в каталозі, якщо це каталог. Це нерозуміння того, як findпрацює.
Kusalananda

1

Я ще не зрозумів проблему, але я можу дати більше інформації.

Як і для Kusalananda, find | grepдзвінок у моїй системі явно швидше, що не має особливого сенсу. Спочатку я припустив якусь проблему буферизації; що запис на консоль сповільнює час до наступного виклику для читання наступного імені файлу. Запис у трубу відбувається дуже швидко: близько 40 Мбіт / с навіть для 32-байтних записів (у моїй досить повільній системі; 300 МБ / с для блоку розміром 1 Мбіт). Таким чином, я припускав, що findможе читати з файлової системи швидше під час запису на трубу (або файл), щоб дві операції зчитування файлових шляхів та запису до консолі могли працювати паралельно (що findяк процес одного потоку не може зробити самостійно.

Це findвина

Порівняння двох дзвінків

:> time find "$HOME"/ -name '*.txt' >/dev/null

real    0m0.965s
user    0m0.532s
sys     0m0.423s

і

:> time find "$HOME"/ >/dev/null

real    0m0.653s
user    0m0.242s
sys     0m0.405s

показує, що findробить щось неймовірно дурне (що б там не було). Це просто виявляється досить некомпетентним у виконанні -name '*.txt'.

Може залежати від співвідношення вводу / виводу

Ви можете подумати, що find -nameвиграє, якщо писати дуже мало. Але Ist просто стає більш соромно find. Він програє, навіть якщо взагалі немає нічого написати проти 200K файлів (13M даних труби) для grep:

time find /usr -name lwevhewoivhol

findgrepХоча може бути настільки ж швидким

Виявляється, findдурість Росії nameне поширюється на інші тести. Використовуйте замість цього регулярний вираз, і проблема зникла:

:> time find "$HOME"/ -regex '\.txt$' >/dev/null     

real    0m0.679s
user    0m0.264s
sys     0m0.410s

Я думаю, це можна вважати помилкою. Хто бажає подати звіт про помилку? Моя версія - знайти (GNU findutils) 4.6.0


Наскільки повторювані ваші таймінги? Якщо ви зробили -nameтест спочатку, то, можливо, він пройшов повільніше через те, що вміст каталогу не кешується. (При тестуванні -nameі -regexя вважаю , вони беруть приблизно в той же час, по крайней мере , один раз ефект кеша було взято до уваги Звичайно , це може бути просто інша версія. find...)
psmears

@psmears Звичайно, я робив ці тести кілька разів. Проблему кешування згадували навіть у коментарях до цього питання до першої відповіді. Моя findверсія - знайти (GNU findutils) 4.6.0
Hauke ​​Laging

Чому дивно, що додавання -name '*.txt'сповільнює find? Він повинен виконати додаткову роботу, протестуючи кожне ім'я файлу.
Barmar

@Barmar З одного боку, цю додаткову роботу можна виконати надзвичайно швидко. З іншого боку, ця додаткова робота економить інші роботи. findмає записувати менше даних. А запис у трубу - це набагато повільніша операція.
Hauke ​​Laging

Запис на диск дуже повільний, запис на трубу не так вже й поганий, він просто копіює в буфер ядра. Зауважте, що у першому тесті пишіть більше, щоб /dev/nullякось використати менше системного часу.
Бармар

0

Зауважте : я припускаю, що ви маєте на увазі find . -name filename(інакше ви шукаєте різні речі; find filenameнасправді шукає шлях, який називається ім'ям файлу , який може містити майже відсутні файли, отже, виходить дуже швидко).


Припустимо, у вас є каталог, що містить п'ять тисяч файлів. У більшості файлових систем ці файли фактично зберігаються в структурі дерева , що дозволяє швидко знайти будь-який один файл.

Так що, коли ви просите , findщоб знайти файл, ім'я якого потрібно тільки перевірка, findбуде просити для цього файлу, і цей файл тільки для базової файлової системи, яка буде читати дуже мало сторінок зі сховища маси. Отже, якщо файлова система стоїть солі, ця операція буде запускатися набагато швидше, ніж обхід всього дерева для отримання всіх записів.

Коли ви просите про просто, findале це саме те, що ви робите, ви обходите все дерево, читаючи. Кожен. Неодружений Вхід. У великих каталогах це може бути проблемою (саме це є причиною того, що декілька програмних програм, які потребують збереження безлічі файлів на диску, створюватимуть «дерева каталогів» з двома або трьома компонентами глибоко: таким чином, кожен лист повинен містити менше файли).


-2

Допустимо, що файл / john / paul / george / ringo / beatles існує, а файл, який ви шукаєте, називається "камені"

find / stones

find порівняє 'beatles' з 'камені' та скине його, коли 's' і 'b' не збігаються.

find / | grep stones

У цьому випадку знахідка перейде '/ john / paul / george / ringo / beatles' в grep, а grep доведеться пройти весь шлях, перш ніж визначити, чи відповідає його.

Таким чином, grep робить набагато більше роботи, тому це займає більше часу


1
Ви спробували це?
Hauke ​​Laging

3
Вартість порівняння рядків (надзвичайно проста та дешева) повністю дефінірована за рахунок IO (або просто систематичного виклику, якщо він кешований), пошукових записів у каталозі.
Мат

grep - це не порівняння рядків, його регулярне порівняння виразів, що означає, що він повинен пройти шлях через всю рядок, поки він не знайде збіг або не досягне кінця. Шукання у каталозі те саме, незалежно від того.
Параноїд

@ Paranoid Hm, про яку версію знахідки ти йдеш ? Це, мабуть, не що інше, як знахідка, до якої я звик в debian.
труба
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.