Що робить "xargs grep"?


25

Я знаю grepкоманду, і я дізнаюся про функціональні можливості xargs, тому я читаю цю сторінку, яка дає кілька прикладів того, як використовувати xargsкоманду.

Мене бентежить останній приклад, приклад 10. У ньому написано "Команда xargs виконує команду grep для пошуку всіх файлів (серед файлів, наданих командою find), що містили рядок" stdlib.h ""

$ find . -name '*.c' | xargs grep 'stdlib.h'
./tgsthreads.c:#include
./valgrind.c:#include
./direntry.c:#include
./xvirus.c:#include
./temp.c:#include
...
...
...

Однак у чому різниця просто використовувати

$ find . -name '*.c' | grep 'stdlib.h'

?

Очевидно, я все ще борюся з тим, що саме робить xargs, тому будь-яка допомога вдячна!


2
Це питання може бути корисним: Коли потрібні XArgs?
TheOdd

Відповіді:


30
$ find . -name '*.c' | grep 'stdlib.h'

Це передає вихід (stdout) * від findдо (stdin of) * grep 'stdlib.h' як текст (тобто назви файлів трактуються як текст). grepробить свою звичайну річ і знаходить відповідні рядки в цьому тексті (будь-які імена файлів, які самі містять шаблон). Вміст файлів ніколи не читається.

$ find . -name '*.c' | xargs grep 'stdlib.h'

Це створює команду, grep 'stdlib.h' для якої кожен результат findє аргументом - тому це буде шукати збіги всередині кожного знайденого файлу find( xargsможна вважати перетворенням його stdin в аргументи для заданих команд) *

Використовуйте -type fкоманду пошуку, інакше ви отримаєте помилки grepщодо відповідності каталогів. Крім того, якщо у іменах файлів є пробіли, вони xargsбудуть погано перекручуватися, тому використовуйте роздільник нулів, додавши -print0та xargs -0для отримання більш надійних результатів:

find . -type f -name '*.c' -print0 | xargs -0 grep 'stdlib.h'

* додав ці додаткові пояснювальні моменти, як це запропоновано в коментарі @cat


2
Ви могли б розглянути з урахуванням (бо здається , ви опустили) ключовий момент , що |труби стандартний висновок в Grep в стандартне введення , який не є такою ж , як аргументи Grep і дає оману результати.
кіт

1
Або використовуйте знахідки GNU find -name '*.c' -exec grep stdlib.h {} +. Я майже ніколи фактично не використовую xargs. Також здивований ніхто не згадав, що xargs служить подібною метою для grep $(find)заміни команд, тому я написав свою відповідь. Пояснення xargs як підстановки команд із меншими обмеженнями та проблемами здається природним.
Пітер Кордес

Одна з ситуацій, на яку я використовую xargs, - це якщо я видаляю багато файлів у результаті знаходження. Якщо ви просто зробите -exec rm, він запустить rm у кожному файлі по одному, що дуже неефективно. Підключення до xargs зробить їх все одночасно однією хвилиною. Обмеження з допомогою say -n50 (робити 50 за один раз) може запобігти переповненню командного рядка (проблема з великою кількістю файлів).
lsd

1
@lsd: Чому б не find -deleteдля цього особливого випадку? Або для інших команд, ніж rm, якщо у вас є GNU find, то -exec some_command {} +згрупуйте в групи, схожі на xargs, замість цього \;поведінка виконання команди окремо для кожного.
Пітер Кордес

@lsd findзапускає команду для кожного файлу, якщо і тільки якщо він використовує -exec command \;Обидва, xargsі -exec command \+викличе команду з максимальною кількістю аргументів, дозволених системою. Іншими словами, вони рівноцінні
Сергій Колодяжний,

6

xargs приймає стандартний вхід і перетворює його в аргументи командного рядка.

find . -name '*.c' | xargs grep 'stdlib.h' дуже схожий на

grep 'stdlib.h' $(find . -name '*.c')  # UNSAFE, DON'T USE

І дасть ті самі результати, якщо список імен файлів не буде занадто довгим для одного командного рядка. (Linux підтримує мегабайти тексту в одному командному рядку, тому зазвичай вам не потрібні xargs.)


Але обидва вони смоктають, оскільки вони ламаються, якщо у ваших іменах є пробіли . Натомість find -print0 | xargs -0працює, але так і робить

find . -name '*.c' -exec grep 'stdlib.h' {} +

Це ніколи не передає назви файлів ніде: findоб'єднує їх у великий командний рядок і запускається grepбезпосередньо.

\;замість +запуску grep окремо для кожного файлу, що значно повільніше. Не робіть цього. Але +це розширення GNU, тому вам потрібно xargsзробити це ефективно, якщо ви не можете припустити, що GNU знаходить.


Якщо ви залишаєтесь xargs, find | grepчи відповідає його шаблон відповідно до списку імен файлів, які findдрукуються.

Тож у цей момент ви можете просто так і зробити find -name stdlib.h. Звичайно -name '*.c' -name stdlib.h, ви не отримаєте жодного результату, оскільки ці зразки не можуть обидві збігатися, і знайти поведінку за замовчуванням до І правил разом.

Замініть lessв будь-який момент процесу, щоб побачити, який вихід дає будь-яка частина трубопроводу.


Подальше читання: http://mywiki.wooledge.org/BashFAQ має кілька чудових матеріалів.


1
GNU xargs також -dповинен встановити роздільник, так що ви можете використовувати -d'\n'для обробки списку, розділеного рядками, що може бути корисним, якщо ви обробляєте список назв файлів у файлі тощо (до тих пір, поки в іменах файлів немає нові рядки в них, тобто.)
ilkkachu

@ilkkachu: Так, нові рядки у назви файлів набагато рідше, ніж пробіли, оскільки вони порушують більшість сценаріїв. myfunc(){ local IFS=$'\n'; fgrep stdlib.h` $ (знайти) ; }також працює з тим же ефектом. Або як (IFS=...; cmd...)однолінійний підзарядник також працює, щоб містити зміни в IFS, не потребуючи їх збереження / відновлення.
Пітер Кордес

@PeterCordes Будь ласка, не робіть подібні command $( find )речі. Проблемні назви файлів з пробілами та спеціальними символами можуть порушити цей тип речей. Щонайменше подвійне процитування підстановки команд.
Сергій Колодяжний

@SergiyKolodyazhnyy: Дякую, що зазначив, що схоже, я насправді рекомендую робити це. Люди, що скумуються, могли скопіювати / вставити це замість того, щоб прочитати наступний розділ. Оновлено для вирішення цього питання.
Пітер Кордес

@SergiyKolodyazhnyy: Або ви відповідали на мій коментар? Зверніть увагу, що я встановив IFSтак, що це рівнозначно використанню xargs '-d\n'. Обробка розширення Glob і метахарактер оболонок відбувається перед ефектами заміни команд, тому я думаю, що це безпечно навіть з назви файлів, які містять $()або >. Погодилися, що використання розділення слів при заміні команд не є хорошою практикою, за винятком разового інтерактивного використання, де ви знаєте щось про назви файлів. Але command "$(find)"корисний лише, якщо ви очікуєте, що він створить рівно 1 ім’я файлу ...
Пітер Кордес

5

Взагалі xargsвикористовується для випадків, коли ви передаваєте (із символом |) щось від однієї команди до іншої ( Command1 | Command2), але вихід з першої команди неправильно приймається як вхід для другої команди.

Зазвичай це відбувається, коли друга команда неправильно обробляє введення даних через Standard In (stdin) (наприклад: Кілька рядків як вхід, спосіб встановлення рядків, символи, що використовуються як вхід, кілька параметрів як вхід, тип даних, отриманий як введення тощо). Щоб дати короткий приклад, протестуйте наступне:

Приклад 1:

ls | echo- Це нічого не зробить, оскільки echoне знає, як обробляти вхід, який він отримує. Тепер у цьому випадку, якщо ми використовуємо xargsйого, буде оброблятися вхід таким чином, щоб можна було правильно обробити echo(наприклад: як єдиний рядок інформації)

ls | xargs echo- Це виведе всю інформацію з lsодного рядка

Приклад 2:

Скажімо, у мене є кілька файлів goLang всередині папки під назвою go. Я б шукав їх приблизно так:

find go -name *.go -type f | echo- Але якби символ труби там і echoв кінці, він би не працював.

find go -name *.go -type f | xargs echo- Тут би це працювало завдяки, xargsале якби я хотів, щоб кожна відповідь findкоманди була в одному рядку, я би зробив наступне:

find go -name *.go -type f | xargs -0 echo- У цьому випадку такий же вихід з findпоказав би показник echo.

Такі команди, як cp, echo, rm, lessі інші, яким потрібен кращий спосіб обробити вхід, отримують перевагу при використанні xargs.


4

xargs використовується для автоматичного генерування аргументів командного рядка на основі (як правило) списку файлів.

Тож розглянемо деякі альтернативи використання наступної xargsкоманди:

find . -name '*.c' -print0 | xargs -0 grep 'stdlib.h'

Є кілька причин використовувати його замість інших варіантів, які спочатку не згадувалися в інших відповідях:

  1. find . -name '*.c' -exec grep 'stdlib.h' {}\;буде генерувати один grepпроцес для кожного файлу - це, як правило, вважається поганою практикою, і може поставити велике навантаження на систему, якщо знайдено багато файлів.
  2. Якщо файлів багато, grep 'stdlib.h' $(find . -name '*.c')команда, ймовірно, не вдасться, оскільки результат $(...)операції перевищить максимальну довжину командного рядка оболонки

Як згадується в інших відповідях, причина використання -print0аргументу findв цьому сценарії та -0аргумент до xargs полягає в тому, що назви файлів з певними символами (наприклад, цитатами, пробілами чи навіть новими рядками) все ще правильно обробляються.

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