Вызов нескольких команд с помощью команды xargs


cat a.txt | xargs -I % echo %

В приведенном выше примере xargs принимает echo % в качестве аргумента команды. Но в некоторых случаях, мне нужно несколько команд для обработки вместо одного, например:

cat a.txt | xargs -I % {command1; command2; ... }

но xargs не принимает эту форму. Одно из решений, которое я знаю, заключается в том, что я могу определить функцию для обертывания команд, но это не конвейер, я не предпочитаю его. Есть ли другое решение?

10 227

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 start OPTION, т. е. как -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 версия получит некоторые недопустимые входные данные, если некоторые строки содержат новые строки, но это неизбежно с файлом, разделенным новой строкой.