Вызов нескольких команд с помощью команды xargs
cat a.txt | xargs -I % echo %
В приведенном выше примере xargs принимает echo %
в качестве аргумента команды. Но в некоторых случаях, мне нужно несколько команд для обработки вместо одного, например:
cat a.txt | xargs -I % {command1; command2; ... }
но xargs не принимает эту форму. Одно из решений, которое я знаю, заключается в том, что я могу определить функцию для обертывания команд, но это не конвейер, я не предпочитаю его. Есть ли другое решение?
10 ответов:
cat a.txt | xargs -I % sh -c 'command1; command2; ...'
обратите внимание, что это бесполезное использование кошки. Я бы написал так:
< a.txt xargs -I % sh -c 'command1; command2; ...'
(да, перенаправление может быть в начале команды.)
предположительно
command1
и/илиcommand2
будет содержать один или несколько элементов%
символы; в противном случае не было бы смысла к доxargs
.
С GNU Parallel вы можете сделать:
cat a.txt | parallel 'command1 {}; command2 {}; ...; '
смотрите вступительные видео, чтобы узнать больше:https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
Это просто еще один подход без xargs ни cat:
while read stuff; do command1 "$stuff" command2 "$stuff" ... done < a.txt
одна вещь, которую я делаю, это добавить .bashrc/.профиль этой функции:
function each() { while read line; do for f in "$@"; do $f $line done done }
тогда вы можете делать такие вещи, как
... | each command1 command2 "command3 has spaces"
, который является менее подробной, чем xargs и остается. Вы также можете изменить функцию, чтобы вставить значение из чтения в произвольном месте в командах для каждого, если вам также нужно это поведение.
можно использовать
cat file.txt | xargs -i sh -c 'command {} | command2 {} && command3 {}'
{} = переменная для каждой строки в текстовом файле
другое возможное решение, которое работает для меня-это что-то вроде -
cat a.txt | xargs bash -c 'command1 $@; command2 $@' bash
обратите внимание на 'bash' в конце - я предполагаю, что он передается как argv[0] в bash. Без него в этом синтаксисе теряется первый параметр к каждой команде. Это может быть любое слово.
пример:
cat a.txt | xargs -n 5 bash -c 'echo -n `date +%Y%m%d-%H%M%S:` ; echo " data: " $@; echo "data again: " $@' bash
Я предпочитаю стиль, который позволяет в режиме сухого хода (без
| sh
):cat a.txt | xargs -I % echo "command1; command2; ... " | sh
работает и с трубами:
cat a.txt | xargs -I % echo "echo % | cat " | sh
немного опоздал на вечеринку.
я использую формат ниже для сжатия моих каталогов с тысячами крошечных файлов перед миграцией. Если вам не нужны одинарные кавычки внутри команд, это должно работать.
С некоторыми изменениями, я уверен, что это будет полезно для кого-то. Проверено в
Cygwin
(babun)find . -maxdepth 1 ! -path . -type d -print0 | xargs -0 -I @@ bash -c '{ tar caf "@@.tar.lzop" "@@" && echo Completed compressing directory "@@" ; }'
find .
найти здесь-maxdepth 1
не заходите в дочерние каталоги! -path .
исключить . / Текущий путь к каталогу-type d
совпадают только каталоги-print0
отдельный вывод по нулевым байтам \0| xargs
труба в xargs-0
входной сигнал null отделенные байты-I @@
местозаполнитель @@. Замените @@ на ввод.bash -c '...'
выполнить команду Bash
группировка&&
выполнить следующую команду только в том случае, если предыдущая команда завершилась успешно (выход 0)финал
;
важно, иначе это будет неудача.выход:
Completed compressing directory ./Directory1 with meta characters in it Completed compressing directory ./Directory2 with meta characters in it Completed compressing directory ./Directory3 with meta characters in it
Обновление 2018 Июля:
если вы любите хаки и играть вокруг, вот что интересно:
echo "a b c" > a.txt echo "123" >> a.txt echo "###this is a comment" >> a.txt cat a.txt myCommandWithDifferentQuotes=$(cat <<'EOF' echo "command 1: $@"; echo 'will you do the fandango?'; echo "command 2: $@"; echo EOF ) < a.txt xargs -I @@ bash -c "$myCommandWithDifferentQuotes" -- @@
выход:
command 1: a b c will you do the fandango? command 2: a b c command 1: 123 will you do the fandango? command 2: 123 command 1: ###this is a comment will you do the fandango? command 2: ###this is a comment
объяснение:
- создайте один сценарий лайнера и сохраните его в переменной
-xargs
читаетa.txt
и выполняет его какbash
скрипт
-@@
убеждается каждый раз вся строка передается
- положить@@
после--
гарантирует@@
принимается в качестве позиционного параметра ввода в , а неbash
startOPTION
, т. е. как-c
сам по себе, что означаетrun command
--
волшебный, он работает со многими другими вещами, т. е.ssh
, дажеkubectl
мой текущий BKM для этого
... | xargs -n1 -I % perl -e 'system("echo 1 %"); system("echo 2 %");'
к сожалению, это использует perl, который менее вероятно будет установлен, чем bash; но он обрабатывает больше входных данных, чем принятый ответ. (Я приветствую вездесущую версию, которая не полагается на perl.)
@Keiththompson's suggestion of
... | xargs -I % sh -c 'command1; command2; ...'
отлично - если у вас нет символа комментария оболочки # в вашем вводе, в этом случае часть первой команды и вся вторая команда будут усеченный.
хэши # могут быть довольно распространенными, если входные данные получены из списка файловой системы, например ls или find, и ваш редактор создает временные файлы с # в их имени.
пример проблемы:
$ bash 1366 $> /bin/ls | cat #Makefile# #README# Makefile README
Ой, вот в чем проблема:
$ bash 1367 $> ls | xargs -n1 -I % sh -i -c 'echo 1 %; echo 2 %' 1 1 1 1 Makefile 2 Makefile 1 README 2 README
Ах, так-то лучше:
$ bash 1368 $> ls | xargs -n1 -I % perl -e 'system("echo 1 %"); system("echo 2 %");' 1 #Makefile# 2 #Makefile# 1 #README# 2 #README# 1 Makefile 2 Makefile 1 README 2 README $ bash 1369 $>
это, кажется, самая безопасная версия.
tr '[\n]' '[]' < a.txt | xargs -r0 /bin/bash -c 'command1 "$@"; command2 "$@";'
(
-0
может быть удален иtr
заменено перенаправлением (или файл может быть заменен на файл с разделением null вместо этого). Это в основном там, так как я в основном используюxargs
Сfind
С-print0
выход) (это также может быть актуальным наxargs
версии без ]' < a.txt | xargs -r0 -n1 /bin/bash -c 'command1 "$@"; command2 "$@";'или с несколько меньшим количеством процессов:
tr '[\n]' '[]' < a.txt | xargs -r0 /bin/bash -c 'for f in "$@"; do command1 "$f"; command2 "$f"; done;'
если у вас есть GNU
xargs
или другой с-P
расширение и вы хотите запустить 32 процесса параллельно, каждый с не более чем 10 параметров для каждой команды:tr '[\n]' '[]' < a.txt | xargs -r0 -n10 -P32 /bin/bash -c 'command1 "$@"; command2 "$@";'
это должно быть устойчивым к любым специальным символам во входных данных. (Если входные данные разделены нулем.) В
tr
версия получит некоторые недопустимые входные данные, если некоторые строки содержат новые строки, но это неизбежно с файлом, разделенным новой строкой.