Коли потрібні xargs?


134

xargsКоманда завжди плутає мене. Чи існує для цього загальне правило?

Розглянемо два приклади нижче:

$ \ls | grep Cases | less

друкує файли, які відповідають "Cases", але для зміни команди на touchзнадобиться xargs:

$ \ls | grep Cases | touch
touch: missing file operand
Try `touch --help' for more information.

$ \ls | grep Cases | xargs touch

Відповіді:


143

Різниця полягає в тому, які дані приймає цільова програма.

Якщо ви просто використовуєте трубу, вона отримує дані про STDIN (стандартний вхідний потік) як необроблену купу даних, яку вона може сортувати по одному рядку за раз. Однак деякі програми не приймають свої команди стандартно в, вони очікують, що це буде прописано в аргументах до команди. Наприклад touchприймає ім'я файлу в якості параметра в командному рядку , наприклад так: touch file1.txt.

Якщо у вас є програма , яка виводить імена файлів на стандартний висновок і хочете використовувати їх в якості аргументів до touch, ви повинні використовувати , xargsякий зчитує дані потоку STDIN і перетворює кожен рядок в просторі , розділених аргументи команди.

Ці дві речі рівнозначні:

# touch file1.txt
# echo file1.txt | xargs touch

Не використовуйте, xargsякщо ви точно не знаєте, що це робить і навіщо це потрібно. Досить часто трапляється так, що є кращий спосіб виконати роботу, ніж використання xargsдля примусового перетворення. Процес перетворення також загрожує потенційними підводними каменями, такими як втеча та розширення слова тощо.


2
Попередження відчуває трохи струни для мене. З двох поширених варіантів потрапляння потоку в командний рядок ( xargsі $(...)), xargs набагато безпечніше, ніж підміна команд. І я не можу пригадати, щоб коли-небудь стикався з законним іменем файлу з новим рядком. Чи не проблеми з розбіганням та розширенням слів є питаннями заміни команд, а не xargs?
camh

6
@camh: Вони потенційні підводні камені з обома. У оболонці вам доведеться турбуватися про розбиття назви файлів на пробіли, вкладки та нові рядки. У xargs вам потрібно лише турбуватися про нові рядки. У xargs, якщо ваш вихід форматований належним чином, ви можете розділити слова / назви файлів на символі NUL замість ( xargs -0), що корисно в поєднанні з find -print0.
Кен Блум

Чи xargsвикликає програму через оболонку з аргументами, розділеними пробілом, чи вона фактично будує список аргументів внутрішньо (наприклад, для використання з execv/ execp)?
detly

1
Він створює його внутрішньо і використовує execvp, тому це безпечно. Крім того, xargs GNU (як використовується в Linux та декількох інших) дозволяє вам вказати новий рядок як ваш роздільник -d \n, хоча BSD xargs (OSX та ін) не підтримує цю опцію.
пухнастий

72

Щоб розширити відповіді, що вже надаються, xargsможна зробити одну цікаву річ, яка стає все більш важливою в сучасній багатоядерній та розподіленій обчислювальній обстановці: вона може паралельно обробляти завдання.

Наприклад:

$ find . -type f -name '*.wav' -print0 |xargs -0 -P 3 -n 1 flac -V8

буде кодувати * .wav => * .flac, використовуючи одразу три процеси ( -P 3).


Ого. Я повинен був знати це тиждень тому, коли я робив абсолютно те саме (крім використання OGG) з 50GiB WAV. :)
Alois Mahdal

чому б не використовувати параметр -exec, який має знахідка?
Євгеній

3
@Evgeny -execПараметр не буде паралельно виконувати завдання.
амфетамахін

Добре зауважити, що -0аргументxargs змушує вважати цей NULLсимвол розмежувачем елемента введення. find -print0виводити елементи, обмежені NULL. Це відмінна практика для імен файлів, які можуть містити пробіли, лапки або інші спеціальні символи.
Дан Даскалеску

24

xargs особливо корисний, коли у вас є список файлових маршрутів на stdin і ви хочете зробити з ними щось. Наприклад:

$ git ls-files "*.tex" | xargs -n 1 sed -i "s/color/colour/g"

Розглянемо цей крок за кроком:

$ git ls-files "*.tex"
tex/ch1/intro.tex
tex/ch1/motivation.tex
....

Іншими словами, наше введення - це список шляхів, до яких ми хочемо щось зробити.

Щоб дізнатись, що робить xargs з цими шляхами, приємним трюком є ​​додавання echoперед вашою командою, наприклад:

$ git ls-files "*.tex" | xargs -n 1 echo sed -i "s/color/colour/g"
sed -i "s/color/colour/g" tex/ch1/intro.tex
sed -i "s/color/colour/g" tex/ch1/motivation.tex
....

-n 1Аргумент змусить xargs перетворити кожен рядок в команду своїх власних. sed -i "s/color/colour/g"Команда замінить всі входження colorз colourзазначеним файлом.

Зауважте, що це працює лише в тому випадку, якщо у ваших контурах немає пробілів. Якщо це зробити, ви повинні використовувати нульові закінчені шляхи як вхід до xargs, передаючи -0прапор. Прикладом використання може бути:

$ git ls-files -z "*.tex" | xargs -0 -n 1 sed -i "s/color/colour/g"

Що робить те саме, що ми описали вище, але також працює, якщо в одному із шляхів є пробіл.

Це працює з будь-якою командою, яка створює назви файлів як вихід, наприклад, findабо locate. Якщо ви все-таки використовуєте його в сховищі git з великою кількістю файлів, можливо, його буде більш ефективно використовувати git grep -lзамість git ls-filesцього:

$ git grep -l "color" "*.tex" | xargs -n 1 sed -i "s/color/colour/g"

git grep -l "color" "*.tex"Команда дасть список файлів «* .tex» , що містять фразу «колір».


1
Щоправда, але якщо ви цього навчилися, ви також повинні навчитися Чому перебирає погану практику пошуку результатів пошуку?
Wildcard

6

Ваш перший аргумент досить добре ілюструє різницю.

\ls | grep Cases | lessдозволяє переглядати список імен файлів, створених lsі grep. Не має значення, що вони бувають іменами файлів, це лише якийсь текст.

\ls | grep Cases | xargs lessдозволяє переглядати файли, імена яких створюються першою частиною команди. xargsприймає список імен файлів як вхідних даних і команду в його командному рядку та запускає команду з іменами файлів у її командному рядку.

Розмірковуючи про використання xargs, майте на увазі, що він очікує, що формат введення буде відформатований дивним чином: пробіл з обмеженим простором, з \, 'і "використовується для цитування (як незвично, тому що \це не особливі внутрішні лапки). Використовуйте лише, xargsякщо ваші імена файлів не містять пробілів або \'".


@Gilles: xargs є -0, --nullможливість обійти пробіл (дуже ймовірно, я дізнався це від вас :), тому я припускаю, що ви посилаєтесь на xargдзвінок без варіантів , але мене спантеличує ваше посилання на цитати. Чи є у вас посилання чи приклад щодо цього? .. (пс | xargs less- це зручна «хитрість» +1 .. дякую ..
Пітер.О

4

У вашому прикладі вам взагалі не потрібно користуватися, xargsоскільки findбудете робити точно і безпечно те, що ви хочете зробити.

Саме ви хочете використовувати find:

find -maxdepth 1 -name '*Cases*' -exec touch {} +

У цьому прикладі -maxdepth 1означає лише пошук у поточному каталозі, не спускайтеся в жодні підкаталоги; Значення за замовчуванням знайдеться у всіх підкаталогах (що часто є тим, що ви хочете), якщо ви не обмежите його maxdepth. Це {}ім'я файлу, який заміниться замість нього, і +є одним із двох маркерів кінцевих команд, іншим є ;. Різниця між ними полягає в тому, що ;означає виконувати команду для кожного файлу по одному, тоді як +означає виконати команду для всіх файлів одночасно. Однак слід зазначити, що ваша оболонка, ймовірно , спробує інтерпретувати ;себе, так що вам потрібно буде , щоб уникнути його або \;або ';'. Так, findє така кількість маленьких роздратовань, як ця, але її сила більше, ніж компенсує це.

І те findй xargsінше складно навчитися спочатку. Щоб допомогти вам навчитися, xargsспробуйте скористатися параметром -pабо, --interactiveякий покаже вам команду, яку вона збирається виконати, і підкаже, хочете ви її виконати чи ні.

Аналогічно з findвами ви можете використовувати -okзамість того, -execщоб підказати вам, чи хочете ви виконати команду.

Однак бувають випадки, коли findне вдасться виконати все, що ти хочеш, і саме там xargsприходить. -execКоманда прийме лише один екземпляр {}появи, тому якщо ви отримаєте помилку, find -type f -exec cp {} {}.bak \;то замість цього можете зробити це так :find -type f -print0 | xargs -0 -l1 -IX cp X X.bak

Ви можете дізнатися більше про виконання команд у посібнику GNU Findutils .

Крім того, я згадав, що findбезпечно робить те, що ви хочете, тому що, коли ви маєте справу з файлами, ви зіткнетеся з пробілами та іншими символами, які можуть спричинити проблеми, xargsякщо ви не використовуєте параметр -0або --nullпоряд із чимось, що генерує елементи введення, закінчені замість нульового символу. пробілів.



Файли файлів @Wildcard з пробілами чи символами, такими як 'або "можуть бути проблематичними, тоді як findці проблеми будуть оброблятися без проблем.
акуліч

Так, я знаю. Дивіться мою відповідь на пов'язане питання . Напевно, я мав би перефразувати це запитання до висловлювання у вищезазначеному коментарі або додати перед ним фразу "Дивіться питання ..." : D
Wildcard

1

xargs(Поряд з find, sort, du, uniq, perlі кілька інших) приймає параметр командного рядка , щоб сказати «STDIN є список файлів, розділених NUL (0x00) байт». Це дозволяє легко обробляти імена файлів з пробілами та іншими забавними символами в них. Імена файлів не містять NUL.


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