Заставить xargs выполнить команду один раз для каждой строки ввода


Как я могу заставить xargs выполнить команду ровно один раз для каждой строки ввода? Это поведение по умолчанию состоит в том, чтобы разделить строки и выполнить команду один раз, передавая несколько строк в каждый экземпляр.

от http://en.wikipedia.org/wiki/Xargs:

найти / путь-тип f-print0 / xargs -0 rm

в этом примере find передает входные данные xargs с длинным списком имен файлов. затем xargs разбивает этот список на подсписки и вызовы rm один раз для каждого подсписка. Это более эффективно, чем эта функционально эквивалентная версия:

найти / путь-тип f-exec rm' {}';

Я знаю, что find имеет флаг "exec". Я просто цитирую иллюстративный пример из другого ресурса.

12 260

12 ответов:

следующее будет работать только в том случае, если у вас нет пробелов во входных данных:

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.

мне кажется, что все существующие ответы на этой странице неверны, включая тот, который отмечен как правильный. Это связано с тем, что вопрос сформулирован неоднозначно.

резюме: если вы хотите выполнить команду " ровно один раз для каждой строки ввода данных," передача всей строки (без новой строки) в команду как один аргумент тогда это лучший UNIX-совместимый способ сделать это:

... | tr '\n' '' | xargs -0 -n1 ...

GNU xargs могут или не могут иметь полезные расширения, которые позволяют вам покончить с tr, но они не доступны на OS X и других UNIX-систем.

теперь для длинного объяснения...


есть два вопроса, которые следует учитывать при использовании xargs:

  1. как он разбивает входные данные на "аргументы"; и
  2. сколько аргументов нужно передать дочерней команде при a время.

чтобы проверить поведение 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 с помощью tr:

$ echo $'one \ntwo\nthree and four' | tr '\n' '' | xargs -0 ./show 
-> "one " "two" "three and four" 

теперь разбор аргументов выглядит нормально, включая конечные пробелы.

наконец, если вы объедините эту технику с -n 1, вы получаете ровно одно выполнение команды в строке ввода, независимо от того, какой вход у вас есть, что может быть еще одним способом взглянуть на исходный вопрос (возможно, самый интуитивный, учитывая название):

$ echo $'one \ntwo\nthree and four' | tr '\n' '' | xargs -0 -n1 ./show 
-> "one " 
-> "two" 
-> "three and four" 

если вы хотите выполнить команду строка (т. е. результат) из find, то, что вам нужно xargs для чего?

попробуй:

findпуть-type f -execyour-command{} \;

где буквальный {} заменяется именем файла и литералом \; нужен find чтобы знать, что пользовательская команда заканчивается там.

EDIT:

(после редактирование вашего вопроса уточняет, что вы знаете о -exec)

С man xargs:

- Lmax-lines
Используйте не более max-lines непустые входные строки в командной строке. Задний пробелы вызывают логическое продолжение входной строки на следующей входной строке. Подразумевает-х.

обратите внимание, что имена файлов, заканчивающиеся пробелами, вызовут у вас проблемы, если вы используете 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

так что если вы не заботитесь о , вам лучше использовать -print0 и -0:

$ find . -type f -print0 | xargs -0L1 wc -l
0 ./c
0 ./c
0 ./b
0 ./a

мне не нравится -L ответ, потому что он не обрабатывает файлы с пробелами в них, что является ключевой функцией find -print0.

echo "file with space.txt" | xargs -L 1 ls
ls: file: No such file or directory
ls: space.txt: No such file or directory
ls: with: No such file or directory

лучшее решение-использовать tr для преобразования новых строк в null () персонажей, а затем использовать ' | xargs -0 ls file with space.txt

еще одна проблема с использованием -L также это не позволяет несколько аргументов для каждого xargs командная вызова. Например, принятое в настоящее время решение будет вызывать /bin/rm для каждой из строк, которая является тонна execs. С помощью , потом -n может использоваться для ограничения количества строк, считанных с входа для каждого вызова утилиты.

find . -name \*.java | tr '\n' '' | xargs -0 wc

это также позволяет фильтровать вывод find до преобразование разрывов в нули.

find . -name \*.xml | grep -v /workspace/ | tr '\n' '' | xargs -0 tar -cf xml.tar

другая альтернатива...

find /path -type f | while read ln; do echo "processing $ln"; done
find path -type f | xargs -L1 command 

все, что вам нужно.

эти два способа также работают и будут работать для других команд, которые не используют find!

xargs -I '{}' rm '{}'
xargs -i rm '{}'

пример использования:

find . -name "*.pyc" | xargs -i rm '{}

удалит все файлы pyc в этом каталоге, даже если файлы pyc содержат пробелы.

следующая команда найдет все файлы (- тип f) в /path и затем скопировать их с помощью cp в текущей папке. Обратите внимание на использование if -I % чтобы указать символ-заполнитель в cp командной строки, так что аргументы могут быть размещены после имени файла.

find /path -type f -print0 | xargs -0 -I % cp % .

протестировано с помощью xargs (GNU findutils) 4.4.0

вы можете ограничить количество строк или аргументов (если между каждым аргументом есть пробелы), используя флаги --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.

Кажется, у меня не хватает репутации, чтобы добавить комментарий ответ Тобии выше, поэтому я добавляю этот "ответ", чтобы помочь тем из нас, кто хочет экспериментировать с xargs то же самое на платформах Windows.

вот пакетный файл windows, который делает то же самое, что и быстро закодированный сценарий "show" Tobia:

@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.

в вашем примере точка передачи вывода find в xargs заключается в том, что стандартное поведение опции find-exec заключается в выполнении команды один раз для каждого найденного файла. Если вы используете find, и вам нужно его стандартное поведение, то ответ прост - не используйте xargs для начала.

выполнить задачу ant clean-все на каждой сборке.xml в текущей или подпапке.

find . -name 'build.xml' -exec ant -f {} clean-all \;