Мне кажется, что все существующие ответы на этой странице неверны, включая ответ, помеченный как правильный. Это связано с тем, что вопрос сформулирован неоднозначно.
Описание: Если вы хотите выполнить команду «ровно один раз для каждой заданной строки ввода», передав всю строку (без новой строки) команде в виде одного аргумента, то это лучший 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
будет еще более эффективным :)