Сделать 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 \;