Заставить xargs выполнить команду один раз для каждой строки ввода
Как я могу заставить xargs выполнить команду ровно один раз для каждой строки ввода? Это поведение по умолчанию состоит в том, чтобы разделить строки и выполнить команду один раз, передавая несколько строк в каждый экземпляр.
от http://en.wikipedia.org/wiki/Xargs:
найти / путь-тип f-print0 / xargs -0 rm
в этом примере find передает входные данные xargs с длинным списком имен файлов. затем xargs разбивает этот список на подсписки и вызовы rm один раз для каждого подсписка. Это более эффективно, чем эта функционально эквивалентная версия:
найти / путь-тип f-exec rm' {}';
Я знаю, что find имеет флаг "exec". Я просто цитирую иллюстративный пример из другого ресурса.
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:
- как он разбивает входные данные на "аргументы"; и
- сколько аргументов нужно передать дочерней команде при 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 -exec
your-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!
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.