2008-10-13 7 views
226

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

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

находка/путь типа F -print0 | xargs -0 rm

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

find/path -type f -exec rm '{}' \;

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

+4

В примере вы предоставляете, `найти/путь типа F -delete` будет еще больше эффективный :) – tzot 2011-12-14 08:34:25

+0

постарайтесь не использовать xargs ... – Naib 2015-03-02 10:04:04

+4

OP, я знаю, что этот вопрос очень старый, но он все же появляется на Google и IMHO, принятый ответ неверен. См. Мой более длинный ответ ниже. – Tobia 2015-03-02 10:11:35

ответ

289
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. 
+8

Для меня это может быть как «xargs -n 1», поскольку тот, который вы дали, показал «слишком длинный список аргументов». – Wernight 2011-09-20 09:14:57

+15

Если параметр «MAX-LINES» опущен, по умолчанию он равен 1, поэтому достаточно «xargs -l». См. `Info xargs`. – Thor 2012-06-30 00:50:31

+0

Также полезно для восстановления всех файлов с помощью git. git ls-files --deleted | xargs -L 1 git checkout - – Eloff 2012-08-20 21:09:00

2

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

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

20

Если вы хотите запустить команду для каждый линии (т.е. результат), идущие от 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
find path -type f | xargs -L1 command 

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

3

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

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

Испытано с xargs (GNU Findutils) 4.4.0

-2

выполнить муравей задачу очистки всех на каждом build.xml на текущем или подпапке.

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

Еще одна альтернатива ...

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

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

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

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

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

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

102

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

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

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

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

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


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

  1. , как делает это разделить вклад в «аргументы»; и
  2. сколько аргументов для передачи дочерней команды за раз.

Чтобы проверить поведение xargs, нам нужна утилита, которая показывает, сколько раз она выполняется и сколько аргументов. Я не знаю, если есть стандартная утилита, чтобы сделать это, но мы можем закодировать его довольно легко в Баш:

#!/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 выполнить команду ровно один раз для каждого аргумента введенного ввода? Его поведение по умолчанию заключается в том, чтобы поместить вход в аргументы и выполнить команду как можно меньше, передав несколько аргументов каждому экземпляру.

тогда ответ -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" 
9

Я не люблю -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 для преобразования новой строки в нуль (\0) символы, а затем использовать xargs -0 аргумент.

echo "file with space.txt" | tr '\n' '\0\ | xargs -0 ls 
file with space.txt 

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

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

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

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

Кажется, у меня не хватает репутации, чтобы добавить комментарий к Tobia's answer above, поэтому я добавляю этот «ответ», чтобы помочь тем, кто из нас хочет, чтобы экспериментировать с xargs так же, как на платформах Windows.

Вот окна пакетный файл, который делает то же самое, как быстро кодированной «шоу» сценарий Тобия в:

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

 Смежные вопросы

  • Нет связанных вопросов^_^