Когда требуется xargs?

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

Рассмотрим два примера ниже:

$ \ls | grep Cases | less

печатает файлы, соответствующие «Cases», но для изменения команды на touch потребуется xargs:

$ \ls | grep Cases | touch
touch: missing file operand
Try `touch --help' for more information.

$ \ls | grep Cases | xargs touch
121 голос | спросил Zaid 19 62011vEurope/Moscow11bEurope/MoscowSat, 19 Nov 2011 20:34:11 +0400 2011, 20:34:11

6 ответов


130

Разница заключается в том, какие данные принимает целевая программа.

Если вы просто используете канал, он получает данные по STDIN (стандартный поток ввода) как сырую груду данных, которые он может сортировать по одной строке за раз. Однако некоторые программы не принимают свои команды по стандарту, они ожидают, что это будет указано в аргументах команды. Например, touch принимает имя файла в качестве параметра в командной строке следующим образом: touch file1.txt.

Если у вас есть программа, которая выводит имена файлов на стандартном и хочет использовать их как аргументы для touch, вы должны использовать xargs, который считывает данные потока STDIN и преобразует каждую строку в аргументы, разделенные пробелом, в команду.

Эти две вещи эквивалентны:

xargs

Не используйте # touch file1.txt # echo file1.txt | xargs touch , если вы точно не знаете, что он делает и почему это необходимо. Часто бывает так, что есть лучший способ выполнить задание, чем использовать xargs для принудительного преобразования. Процесс преобразования также чреват потенциальными ловушками, такими как экранирование и расширение слова и т. Д.

ответил Caleb 19 62011vEurope/Moscow11bEurope/MoscowSat, 19 Nov 2011 20:40:59 +0400 2011, 20:40:59
62

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

Например:

$ find . -type f -name '*.wav' -print0 |xargs -0 -P 3 -n 1 flac -V8

будет кодировать * .wav => * .flac, используя сразу три процесса (-P 3).

ответил amphetamachine 20 72011vEurope/Moscow11bEurope/MoscowSun, 20 Nov 2011 01:02:15 +0400 2011, 01:02:15
24

xargs особенно полезен, когда у вас есть список файловых путей на stdin и вы хотите что-то с ними делать. Например:

$ git ls-files "*.tex" | xargs -n 1 sed -i "s/color/colour/g"

Давайте рассмотрим это шаг за шагом:

$ git ls-files "*.tex"
tex/ch1/intro.tex
tex/ch1/motivation.tex
....

Другими словами, наш вход - это список путей, которые мы хотим что-то сделать.

Чтобы узнать, что xargs делает с этими путями, хороший трюк заключается в том, чтобы добавить echo перед вашей командой, например:

$ git ls-files "*.tex" | xargs -n 1 echo sed -i "s/color/colour/g"
sed -i "s/color/colour/g" tex/ch1/intro.tex
sed -i "s/color/colour/g" tex/ch1/motivation.tex
....

Аргумент -n 1 заставит xargs превратить каждую строку в собственную команду. Команда sed -i "s/color/colour/g" заменит все вхождения color на colour для указанного файла. р>

Обратите внимание, что это работает только в том случае, если у вас нет пробелов в ваших путях. Если вы это сделаете, вы должны использовать пути с нулевым завершением в качестве ввода в xargs, передав флаг -0. Пример использования:

$ git ls-files -z "*.tex" | xargs -0 -n 1 sed -i "s/color/colour/g"

Что делает то же, что мы описали выше, но также работает, если один из путей имеет в нем пробел.

Это работает с любой командой, которая выдает имена файлов как выходные, такие как find или locate. Если вы действительно используете его в репозитории git с большим количеством файлов, возможно, более эффективно использовать его с git grep -l вместо git ls-files, так:

$ git grep -l "color" "*.tex" | xargs -n 1 sed -i "s/color/colour/g"

Команда git grep -l "color" "*.tex" предоставит список файлов * .tex, содержащих фразу «color».

ответил Sverre Rabbelier 20 72011vEurope/Moscow11bEurope/MoscowSun, 20 Nov 2011 00:54:45 +0400 2011, 00:54:45
6

Ваш первый аргумент хорошо иллюстрирует разницу.

\ls | grep Cases | less позволяет просматривать список имен файлов, созданных с помощью ls и grep. Не имеет значения, что они являются именами файлов, это всего лишь текст.

\ls | grep Cases | xargs less позволяет просматривать файлы, имена которых создаются первой частью команды. xargs принимает список имен файлов в качестве ввода и команду в командной строке и запускает команду с именами файлов в командной строке its .

При рассмотрении использования xargs помните, что он ожидает ввода, отформатированного странным образом: пробел-разделитель, с \, ' и ", используемый для цитирования (необычным способом, потому что \ не является специальным внутренним кавычком). Используйте только xargs, если вы имеете имена файлов не содержат пробелов или \'".

ответил Gilles 20 72011vEurope/Moscow11bEurope/MoscowSun, 20 Nov 2011 04:45:41 +0400 2011, 04:45:41
4

В вашем примере вам вообще не нужно использовать xargs, так как find будет делать точно и безопасно то, что вы хотите сделать.

Именно то, что вы хотите использовать find:

find -maxdepth 1 -name '*Cases*' -exec touch {} +

В этом примере -maxdepth 1 означает только поиск в текущем каталоге, не спускайтесь в какие-либо подкаталоги; по умолчанию find будет выглядеть во всех подкаталогах (что часто бывает так, как вы хотите), если вы не ограничиваете его с помощью maxdepth. {} - это имя файла, который будет заменен вместо него, а + - один из двух маркеров конца команды, а другой - ;. Разница между ними заключается в том, что ; означает выполнение команды для каждого файла по одному, тогда как + означает выполнение команды во всех файлах одновременно. Обратите внимание, однако, что ваша оболочка, вероятно, попытается интерпретировать сам ;, поэтому вам нужно будет избежать ее с помощью \; или ';'. Да, find имеет ряд небольших неприятностей, подобных этому, но его мощность более чем компенсирует его.

Оба find и xargs сложны для изучения вначале. Чтобы помочь вам изучить xargs, попробуйте использовать опцию -p или --interactive, которая покажет вам команду, которую она собирается выполнить, и предложит вам независимо от того, хотите ли вы его запустить.

Аналогично find вместо -ok вы можете использовать -exec, чтобы спросить, хотите ли вы выполнить команду.

Однако есть моменты, когда find не сможет делать все, что вам нужно, и это то, где приходит xargs. -exec будет принимать только один экземпляр {}, поэтому, если вы получите сообщение об ошибке с find -type f -exec cp {} {}.bak \; чтобы вы могли сделать это так: find -type f -print0 | xargs -0 -l1 -IX cp X X.bak

Вы можете узнать больше о Run Commands в руководстве GNU Findutils .

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

ответил aculich 23 32011vEurope/Moscow11bEurope/MoscowWed, 23 Nov 2011 19:36:36 +0400 2011, 19:36:36
1

xargs (вместе с find, sort, du, uniq), perl и несколько других) принимает ключ командной строки, чтобы сказать: «STDIN имеет список файлов, разделенных байтом NUL (0x00)». Это упрощает обработку имен файлов с пробелами и другими забавными символами. Имена файлов не содержат NUL.

ответил waltinator 20 72011vEurope/Moscow11bEurope/MoscowSun, 20 Nov 2011 03:05:58 +0400 2011, 03:05:58

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132