Сделать xargs выполнить команду один раз для каждой строки ввода
Как я могу заставить xargs выполнить команду ровно один раз для каждой строки ввода?
Это поведение по умолчанию состоит в том, чтобы разбить строки и выполнить команду один раз, передавая несколько строк каждому экземпляру.
Из http://en.wikipedia.org/wiki/Xargs:
find/path -type f -print0 | xargs -0 rm
В этом примере find отправляет входные данные xargs с длинным списком имен файлов. Затем xargs разбивает этот список на подсписки и вызывает rm один раз для каждого подсписок. Это более эффективно, чем эта функционально эквивалентная версия:
find/path -type f -exec rm '{}' \;
Я знаю, что find имеет флаг "exec". Я просто цитирую иллюстративный пример из другого ресурса.
Ответы
Ответ 1
Следующее будет работать, только если у вас нет пробелов в вводе:
xargs -L 1
xargs --max-lines=1 # synonym for the -L option
со страницы руководства:
-L max-lines
Use at most max-lines nonblank input lines per command line.
Trailing blanks cause an input line to be logically continued on
the next input line. Implies -x.
Ответ 2
Мне кажется, что все существующие ответы на этой странице неверны, в том числе отмеченные как правильные. Это связано с тем, что вопрос неоднозначно сформулирован.
Резюме: Если вы хотите выполнить команду "ровно один раз для каждой строки ввода", передав всю строку (без новой строки) команде в качестве единственного аргумента, тогда это лучший совместимый с UNIX способ:
... | tr '\n' '\0' | xargs -0 -n1 ...
GNU xargs
может иметь или не иметь полезных расширений, которые позволяют покончить с tr
, но они недоступны в OS X и других системах UNIX.
Теперь для подробного объяснения...
При использовании xargs следует учитывать два вопроса:
- как он разделяет входные данные на "аргументы"; и
- сколько аргументов передается дочерней команде за раз.
Чтобы проверить поведение xargs, нам нужна утилита, которая показывает, сколько раз она выполняется и сколько аргументов. Я не знаю, есть ли для этого стандартная утилита, но мы можем легко ее закодировать в bash:
#!/bin/bash
echo -n "-> "; for a in "[email protected]"; do echo -n "\"$a\" "; done; echo
Предполагая, что вы сохраните его как show
в текущем каталоге и сделайте его исполняемым, вот как он работает:
$ ./show one two 'three and four'
-> "one" "two" "three and four"
Теперь, если исходный вопрос действительно касается пункта 2. выше (как я думаю, после прочтения его несколько раз), и его нужно читать следующим образом (изменения выделены полужирным шрифтом):
Как я могу заставить xargs выполнить команду ровно один раз для каждого аргумента введенного ввода? Его поведение по умолчанию состоит в том, чтобы вырезать ввод в аргументы и выполнить команду как можно меньше, передавая несколько аргументов для каждого экземпляра. p >
тогда ответ будет -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"
Ответ 3
Если вы хотите запустить команду для каждой строки (т.е. результат), исходящей от find
, то что вам нужно xargs
для?
Try:
find
путь -type f -exec
ваша команда {} \;
где буквальный {}
получает замену имени файла, а буква \;
требуется для find
, чтобы знать, что пользовательская команда завершена.
EDIT:
(после редактирования вашего вопроса, уточняющего, что вы знаете о -exec
)
От man xargs
:
-L max-lines
Используйте в большинстве максимальных строк непустые входные строки для каждой командной строки. Скользящий пробелы вызывают логическую продолжение входной строки в следующей строке ввода. Имплицирует -x.
Обратите внимание, что имена файлов, заканчивающиеся пробелами, могут вызвать проблемы, если вы используете xargs
:
$ mkdir /tmp/bax; cd /tmp/bax
$ touch a\ b c\ c
$ find . -type f -print | xargs -L1 wc -l
0 ./c
0 ./c
0 total
0 ./b
wc: ./a: No such file or directory
Итак, если вам не нужна опция -exec
, лучше использовать -print0
и -0
:
$ find . -type f -print0 | xargs -0L1 wc -l
0 ./c
0 ./c
0 ./b
0 ./a
Ответ 4
Как я могу заставить xargs выполнять команду ровно один раз для каждой заданной строки ввода?
Мне не нравится ответ -L 1
, потому что он не обрабатывает файлы с пробелами в них, что является ключевой функцией find -print0
.
echo "file with space.txt" | xargs -L 1 ls
ls: file: No such file or directory
ls: with: No such file or directory
ls: space.txt: No such file or directory
Лучшее решение - использовать tr
для преобразования новых строк в нулевые (\0
) символы, а затем использовать аргумент xargs -0
.
echo "file with space.txt" | tr '\n' '\0' | xargs -0 ls
file with space.txt
Если вам необходимо ограничить количество вызовов, вы можете использовать аргумент -n 1
, чтобы сделать один вызов для программы для каждого ввода:
echo "file with space.txt" | tr '\n' '\0' | xargs -0 -n 1 ls
Это также позволяет фильтровать вывод команды find до преобразования разрывов в нули.
find . -name \*.xml | grep -v /target/ | tr '\n' '\0' | xargs -0 tar -cf xml.tar
Ответ 5
Другая альтернатива...
find /path -type f | while read ln; do echo "processing $ln"; done
Ответ 6
Эти два способа также работают и будут работать для других команд, которые не используют find!
xargs -I '{}' rm '{}'
xargs -i rm '{}'
пример использования:
find . -name "*.pyc" | xargs -i rm '{}'
удалит все файлы pyc в этом каталоге, даже если файлы pyc содержат пробелы.
Ответ 7
find path -type f | xargs -L1 command
- это все, что вам нужно.
Ответ 8
Следующая команда найдет все файлы (-type f) в /path
, а затем скопирует их с помощью cp
в текущую папку. Обратите внимание на использование if -I %
для указания символа-заполнителя в командной строке cp
, чтобы аргументы могли быть помещены после имени файла.
find /path -type f -print0 | xargs -0 -I % cp % .
Протестировано с помощью xargs (GNU findutils) 4.4.0
Ответ 9
Вы можете ограничить количество строк или аргументов (если между каждым аргументом есть пробелы), используя флаги -max-lines или -max-args соответственно.
-L max-lines
Use at most max-lines nonblank input lines per command line. Trailing blanks cause an input line to be logically continued on the next input
line. Implies -x.
--max-lines[=max-lines], -l[max-lines]
Synonym for the -L option. Unlike -L, the max-lines argument is optional. If max-args is not specified, it defaults to one. The -l option
is deprecated since the POSIX standard specifies -L instead.
--max-args=max-args, -n max-args
Use at most max-args arguments per command line. Fewer than max-args arguments will be used if the size (see the -s option) is exceeded,
unless the -x option is given, in which case xargs will exit.
Ответ 10
Кажется, у меня нет достаточной репутации, чтобы добавить комментарий к ответ Tobia выше, поэтому я добавляю этот "ответ", чтобы помочь тем из нас, кто хочет экспериментируйте с xargs
таким же образом на платформах Windows.
Вот пакетный файл Windows, который делает то же самое, что и Tobia, быстро закодированный "show" script:
@echo off
REM
REM cool trick of using "set" to echo without new line
REM (from: http://www.psteiner.com/2012/05/windows-batch-echo-without-new-line.html)
REM
if "%~1" == "" (
exit /b
)
<nul set /p=Args: "%~1"
shift
:start
if not "%~1" == "" (
<nul set /p=, "%~1"
shift
goto start
)
echo.
Ответ 11
Ответы @Draemon кажутся правильными с "-0" даже с пробелом в файле.
Я пробовал команду xargs и обнаружил, что "-0" отлично работает с "-L". обрабатываются даже пробелы (если ввод был завершен нулем). Ниже приведен пример:
#touch "file with space"
#touch "file1"
#touch "file2"
Следующее разделит пустые значения и выполнит команду для каждого аргумента в списке:
#find . -name 'file*' -print0 | xargs -0 -L1
./file with space
./file1
./file2
поэтому -L1
выполнит аргумент для каждого символа с нулевым символом в конце, если используется с "-0". Чтобы увидеть разницу попробуйте:
#find . -name 'file*' -print0 | xargs -0 | xargs -L1
./file with space ./file1 ./file2
даже это будет выполнено один раз:
#find . -name 'file*' -print0 | xargs -0 | xargs -0 -L1
./file with space ./file1 ./file2
Команда будет выполнена один раз, так как "-L" теперь не разбивается на нулевой байт. вам нужно предоставить оба "-0" и "-L" для работы.
Ответ 12
В вашем примере точка вывода вывода find на xargs заключается в том, что стандартное поведение параметра find -exec заключается в выполнении команды один раз для каждого найденного файла. Если вы используете find, и вы хотите его стандартное поведение, тогда ответ прост - не используйте xargs для начала.
Ответ 13
выполнить ant задачу clean-all для каждого файла build.xml в текущей или подпапке.
find . -name 'build.xml' -exec ant -f {} clean-all \;