Зачем нужен xargs?

24

Предположим, я хочу удалить все файлы в каталоге, кроме одного с именем "notes.txt". Я бы сделал это с конвейером ls | grep -v "notes.txt" | xargs rm. Зачем мне нужен xargs, если на выходе второго канала указан вход, который должен использовать rm?

Для сравнения, конвейер echo "#include <knowledge.h>" | cat > foo.cвставляет отраженный текст в файл без использования xargs. В чем разница между этими двумя трубопроводами?

seewalker
источник
3
Вы не должны использовать ls | grep -v "notes.txt" | xargs rmдля удаления все, кроме notes.txtили вообще, никогда не анализировать lsвывод . Ваша команда прервется, если, например, в одном файле будет пробел. Более безопасный путь - rm !(notes.txt)в Bash (с shopt -s extglobсетом) или rm ^notes.txtв Zsh (с EXTENDED_GLOB) и т. Д.
slhck
Чтобы избежать пробелов, вы могли бы сделать find . -maxdepth 1 -mindepth 1 -print0 | xargs -0вместо ls | xargs:-)
flob

Ответы:

33

Вы путаете два очень разных вида ввода: STDIN и аргументы. Аргументы представляют собой список строк, предоставляемых команде при ее запуске, обычно путем указания их после имени команды (например, echo these are some argumentsили rm file1 file2). STDIN, с другой стороны, представляет собой поток байтов (иногда текст, иногда нет), который команда может (необязательно) прочитать после ее запуска. Вот несколько примеров (обратите внимание, что они catмогут принимать либо аргументы, либо STDIN, но это делает с ними разные вещи):

echo file1 file2 | cat    # Prints "file1 file2", since that's the stream of
                          # bytes that echo passed to cat's STDIN
cat file1 file2    # Prints the CONTENTS of file1 and file2
echo file1 file2 | rm    # Prints an error message, since rm expects arguments
                         # and doesn't read from STDIN

xargs можно рассматривать как преобразование входных данных в стиле STDIN в аргументы:

echo file1 file2 | cat    # Prints "file1 file2"
echo file1 file2 | xargs cat    # Prints the CONTENTS of file1 and file2

echo фактически делает более или менее обратное: он преобразует свои аргументы в STDOUT (который может быть передан в STDIN какой-то другой команды):

echo file1 file2 | echo    # Prints a blank line, since echo doesn't read from STDIN
echo file1 file2 | xargs echo    # Prints "file1 file2" -- the first echo turns
                                 # them from arguments into STDOUT, xargs turns
                                 # them back into arguments, and the second echo
                                 # turns them back into STDOUT
echo file1 file2 | xargs echo | xargs echo | xargs echo | xargs echo    # Similar,
                                 # except that it converts back and forth between
                                 # args and STDOUT several times before finally
                                 # printing "file1 file2" to STDOUT.
Гордон Дэвиссон
источник
9

catпринимает данные от STDINи rmне делает. Для таких команд вам нужно xargsперебирать STDINпострочно и выполнять команды с параметрами командной строки.

Алекс П.
источник