Когда использовать xargs при передаче?

Я новичок в bash и пытаюсь понять, как использовать xargs, что для меня до сих пор не ясно. Например:

history | grep ls

Здесь я ищу команду ls в своей истории. В этой команде я не использовал xargs и все работало нормально.

find /etc - name "*.txt" | xargs ls -l

Я этот, мне пришлось использовать xargs, но я до сих пор не могу понять разницу и не могу правильно решить, когда использовать xargs, а когда нет.


person Sara Hamad    schedule 23.02.2016    source источник
comment
Суть в том, что существует ограничение на размер команд командной строки, которые могут принимать команды. (обычно это жестко запрограммированное число 128KiB) см. Что определяет максимальный размер одного аргумента команды?. Когда ваша командная строка превышает этот размер, вы можете использовать xargs, который разбивает ее и обрабатывает команду за вас.   -  person David C. Rankin    schedule 24.02.2016


Ответы (5)


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

xargs берет данные из стандартного ввода и выполняет команду. По умолчанию данные добавляются в конец команды в качестве аргумента. Однако его можно вставить куда угодно, используя заполнитель для ввода. Традиционный заполнитель — {}; используя это, ваша примерная команда может быть записана как:

find /etc -name "*.txt" | xargs -I {} ls -l {}

Если у вас есть 3 текстовых файла в /etc, вы получите полный список каждого из них. Конечно, вы могли бы просто написать ls -l /etc/*.txt и избавить себя от хлопот.

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

find /etc -name "*.txt" | xargs -I {} mv {} {}.bak

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

find /etc -print0 -name "*.txt" | xargs -I {} -0 mv {} {}.bak

Мое личное мнение таково, что почти всегда есть альтернативы использованию xargs, и вам будет лучше их изучить.

person miken32    schedule 23.02.2016
comment
Если вы найдете это полезным, я использую это: gitlab.com/es20490446e/args - person Alberto Salvia Novella; 18.06.2020

Когда вы используете конвейер без xargs, фактические данные передаются в следующую команду. С другой стороны, при использовании конвейера с xargs фактические данные рассматриваются как параметр следующей команды. Чтобы привести конкретный пример, скажем, у вас есть папка с a.txt и b.txt. a.txt содержит только одну строку "hello world!", а b.txt просто пусто.

Если вы сделаете

ls | grep txt

вы получите вывод:

a.txt
b.txt

Тем не менее, если вы сделаете

ls | xargs grep txt

вы ничего не получите, поскольку ни файл a.txt, ни файл b.txt не содержат слова txt.

Если команда

ls | xargs grep hello

вы получите:

hello world!

Это связано с тем, что с xargs два имени файла, заданные ls, передаются в grep в качестве аргументов, а не фактическое содержимое.

person Nothing More    schedule 02.06.2019

Краткий ответ: пока избегайте xargs. Вернитесь к xargs, когда вы напишете десятки или сотни сценариев.

Команды могут получать ввод от параметров (например, rm bad_example) или могут получать ввод от stdin (не только y в вопросе после rm -i is_this_bad_too, но и read answer). Другие команды, такие как grep и sed, будут искать параметры, и когда параметры не отображают ввод, переключаться на ввод.
Ваш пример grep отлично работает при чтении со стандартного ввода, ничего особенного не требуется.
Вашему ls нужен вывод find в качестве параметра. xargs — это всего лишь один из способов изменить ситуацию. Используйте man xargs для получения дополнительной информации об xargs. Альтернативы:

find /etc -name "*.txt" -exec ls -l {} \;
find /etc -name "*.txt" -ls
ls -l $(find /etc -name "*.txt" )
ls /etc/*.txt

Сначала попробуйте посмотреть, какая из этих команд лучше всего подходит, когда у вас есть a nasty filename with spaces.txt в /etc.

person Walter A    schedule 23.02.2016

GNU Parallel может делать то же самое, что и xargs, но не имеет сломанных и пригодных для эксплуатации «функций».

Вы можете изучить GNU Parallel, просмотрев примеры http://www.gnu.org/software/parallel/man.html#EXAMPLE:-Working-as-xargs--n1.-Argument-appending и просмотрите руководство http://www.gnu.org/software/parallel/parallel_tutorial.html

person Ole Tange    schedule 23.02.2016

xargs(1) опасен (сломан, эксплуатируем и т. д.) при чтении входных данных, не разделенных NUL.

Если вы работаете с именами файлов, используйте вместо этого find's -exec [команда] {} +. Если вы можете получить вывод с разделителями NUL, используйте xargs -0.

person Rany Albeg Wein    schedule 23.02.2016
comment
@SaraHamad: вы можете получить строки с нулевым завершением из своей находки, добавив -print0 в свою командную строку. Проверьте свою справочную страницу. (есть в большинстве новых версий) - person shellter; 24.02.2016
comment
Не лучше ли было бы читать строку IFS= read -r -d '', чтобы гарантировать чтение ввода, разделенного нулевым значением? - person Dirk Herrmann; 24.02.2016
comment
@DirkHerrmann Вы правы. Хотя это я написал для общего случая. Я удалил эту часть в соответствии с вашим комментарием. - person Rany Albeg Wein; 24.02.2016
comment
Быть опасным (сломанным, эксплойтным и т. д.) не означает, что одноразовые скрипты не очень полезны для измельчения десятков файлов :) - person Jakub M.; 24.02.2016
comment
@JakubM. Стереть в прах ! - person Rany Albeg Wein; 24.02.2016
comment
Это может быть правдой, но ничуть не поможет ОП с этим вопросом! - person cmhteixeira; 21.02.2021
comment
@CarlosTeixeira Я не согласен, но спасибо, что поделились своим мнением с общественностью :) - person Rany Albeg Wein; 21.02.2021