Мені здається, всі існуючі відповіді на цій сторінці помилкові, включаючи таку, що зазначена як правильна. Це випливає з того, що питання неоднозначно сформульовано.
Підсумок: Якщо ви хочете виконати команду «рівно один раз для кожного введеного рядка», передаючи всю команду (без нового рядка) команді як єдиний аргумент, то це найкращий сумісний з UNIX спосіб:
... | tr '\n' '\0' | xargs -0 -n1 ...
GNU xargs
може мати або не мати корисні розширення, які дозволяють вам усунути tr
, але вони недоступні для OS X та інших систем UNIX.
Тепер для довгого пояснення ...
При використанні xargs необхідно враховувати дві проблеми:
- як він розділяє вхід на "аргументи"; і
- скільки аргументів, щоб одночасно передати дочірню команду.
Щоб перевірити поведінку xargs, нам потрібна утиліта, яка показує, скільки разів вона виконується і скільки аргументів. Я не знаю, чи є стандартна утиліта для цього, але ми можемо це легко зашифрувати в bash:
#!/bin/bash
echo -n "-> "; for a in "$@"; do echo -n "\"$a\" "; done; echo
Припустимо, що ви збережете його як show
у вашому поточному каталозі та зробите його виконуваним, ось як це працює:
$ ./show one two 'three and four'
-> "one" "two" "three and four"
Тепер, якщо оригінальне запитання справді стосується пункту 2. вище (як я думаю, воно є, прочитавши його кілька разів) і його слід прочитати так (зміни жирним шрифтом):
Як я можу змусити xargs виконати команду рівно один раз для кожного заданого аргументу ? Його поведінка за замовчуванням полягає в збиранні введення в аргументи і виконанні команди якомога менше разів , передаючи кілька аргументів кожному екземпляру.
то відповідь є -n 1
.
Порівняємо поведінку за замовчуванням xargs, яка розбиває вхід на пробіл і викликає команду якомога менше разів:
$ echo one two 'three and four' | xargs ./show
-> "one" "two" "three" "and" "four"
та її поведінка з -n 1
:
$ echo one two 'three and four' | xargs -n 1 ./show
-> "one"
-> "two"
-> "three"
-> "and"
-> "four"
Якщо, з іншого боку, початкове запитання стосувалося пункту 1. Розбиття входу, і його слід було прочитати так (багато людей, що приходять сюди, здається, вважають, що це так, або плутають два питання):
Як я можу зробити xargs виконати команду з рівно одним аргументом для кожного рядка введення даного? Його поведінка за замовчуванням - це стикування ліній навколо пробілу .
то відповідь більш тонка.
Можна було б подумати, що це -L 1
може допомогти, але виявляється, що це не змінює розбір аргументів. Він виконує команду лише один раз для кожного вхідного рядка з стільки аргументів, скільки було у цьому рядку введення:
$ echo $'one\ntwo\nthree and four' | xargs -L 1 ./show
-> "one"
-> "two"
-> "three" "and" "four"
Мало того, але якщо рядок закінчується пробілом, він додається до наступного:
$ echo $'one \ntwo\nthree and four' | xargs -L 1 ./show
-> "one" "two"
-> "three" "and" "four"
Зрозуміло, -L
справа не в тому, щоб змінити те, як xargs розбиває вхід на аргументи.
Єдиний аргумент, який робить це в крос-платформному режимі (виключаючи розширення GNU), це те -0
, що розбиває вхід навколо байтів NUL.
Тоді, лише питання перекладу нових рядків до NUL за допомогою tr
:
$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 ./show
-> "one " "two" "three and four"
Тепер аналіз аргументів виглядає добре, включаючи пробіли пробігу.
Нарешті, якщо ви поєднаєте цю техніку з -n 1
, ви отримаєте рівно одне виконання команд у кожному рядку введення, який би ви не входили, що може бути ще одним способом розгляду вихідного питання (можливо, найбільш інтуїтивного, з огляду на назву):
$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 -n1 ./show
-> "one "
-> "two"
-> "three and four"
find /path -type f -delete
було б ще ефективніше :)