Выполнить команду один раз в строке ввода?

162

Я хочу выполнить команду Java один раз для каждого матча ls | grep pattern -. В этом случае, я думаю, я мог бы сделать, find pattern -exec java MyProg '{}' \;но мне любопытно, в общем случае - есть ли простой способ сказать «выполнить команду один раз для каждой строки стандартного ввода»? (В рыбе или баш.)

Xodarap
источник

Ответы:

92

Вот что xargsделает.

... | xargs command
Кит
источник
25
Не совсем. printf "foo bar\nbaz bat" | xargs echo wheeдаст whee foo bar baz bat. Может быть, добавить -Lили -nварианты?
январь
3
@ Jander Вопрос был довольно общий, поэтому я дал общий инструмент. Правда, вам придется корректировать его поведение с помощью опций в зависимости от конкретных обстоятельств.
Кит
4
... | tr '\ n' '\ 0' | xargs -0
vrdhn
7
как, «конкретные обстоятельства, которые дают правильный ответ на вопрос». :)
mattdm
7
Если вы хотите увидеть правильный способ сделать это с помощью xargs, см. Мой ответ ниже.
Михаил Гольдштейн
167

Принятый ответ имеет право идеи, но ключ , чтобы пройти xargsна -n1переключатель, что означает «Выполнить команду один раз в строку вывода:»

cat file... | xargs -n1 command

Или, для одного входного файла вы можете полностью исключить канал catи просто использовать:

<file xargs -n1 command
Михаил Гольдштейн
источник
1
Также интерес представляет способность xargsк не работать , если stdinпусто: --no-run-if-empty -rЕсли стандартный ввод не содержит никаких nonblanks, не выполнить команду. Обычно команда запускается один раз, даже если нет ввода. Эта опция является расширением GNU.
Ронан Жоуч
4
Как вы получаете доступ к линии внутри command?
BT
Это правильное использование xargs. Без -n1 он работает только с командами, которые обрабатывают списки параметров как множественные вызовы, что не все делают.
masterxilo
3
printf "foo bar \ nbaz bat" | xargs -n1 эхо, которое делится по словам, а не по строкам
Gismo Ranas
112

В Bash или любой другой оболочке в стиле Bourne (ash, ksh, zsh,…):

while read -r line; do command "$line"; done

read -rчитает одну строку из стандартного ввода ( readбез -rинтерпретации обратной косой черты, вы этого не хотите). Таким образом, вы можете сделать одно из следующего:

$ command | while read -r line; do command "$line"; done  

$ while read -r line; do command "$line"; done <file
Стивен Д
источник
6
Когда я попробовал, tail -f syslog | grep -e something -e somethingelse| while read line; do echo $line; doneэто не сработало. Он работал с файлом, переданным в whileцикл, работал только с тем tail -f, работал только с grep, но не с обоими каналами. Предоставление grepв --line-bufferedварианте сделал его работу
Это работает также, когда каждая строка должна быть отправлена ​​на стандартный ввод:command | while read -r line; do echo "$line" | command ; done
Ден
21

Я согласен с Китом, xargs - самый общий инструмент для работы.

Я обычно использую трехэтапный подход.

  • делать базовые вещи, пока у вас не будет то, что вы хотели бы работать
  • подготовьте строку с помощью awk, чтобы он получил правильный синтаксис
  • тогда пусть xargs выполнит его, может быть, с помощью bash.

Есть меньшие и более быстрые способы, но эти способы почти всегда работают.

Простой пример:

ls | 
grep xls | 
awk '{print "MyJavaProg --arg1 42 --arg2 "$1"\0"}' | 
xargs -0 bash -c

2 первые строки выбирают несколько файлов для работы, затем awk готовит красивую строку с командой для выполнения и некоторыми аргументами, а $ 1 - это первый вход столбца из канала. И, наконец, я убедился, что xargs отправляет эту строку в bash, которая просто выполняет ее.

Это немного излишне, но этот рецепт помог мне во многих местах, так как он очень гибкий.

Johan
источник
6
Обратите внимание, xargs -0что в качестве разделителя записей используется нулевой байт, поэтому ваш оператор печати на awk должен выглядеть следующим образомprintf("MyJavaProg --args \"%s\"\0",$1)
glenn jackman
@glenn: пропустил нулевой символ, обновит ответ
Johan
@ Йохан не имеет большого значения, но если вы используете его, awkвы можете сделать так, чтобы он соответствовал шаблону и пропустил, grep например,ls | awk '/xls/ {print...
Эрик Ренуф,
15

GNU Parallel создан для такого рода задач. Самое простое использование:

cat stuff | grep pattern | parallel java MyProg

Посмотрите вступительное видео, чтобы узнать больше: http://www.youtube.com/watch?v=OpaiGYxkSuQ

Оле Танге
источник
1
Никакой реальной необходимости catздесь нет, так как вы grepможете непосредственно прочитать файл
Эрик Ренуф,
1
Спасибо за ссылку, я не обязательно согласен с тем, что ее легче читать, но приятно знать, что она рассматривалась независимо. Я только сейчас немного придумаю, что ссылка на самом деле здесь не применима, так как альтернатива не совсем, < stuff grep patternно grep pattern stuffбез перенаправления или cat не требуется вообще. Тем не менее, это существенно не меняет ваш аргумент, и если вы думаете, что будет яснее всегда использовать вещи в трубе, которая начинается с cat, тогда власть для вас
Эрик Ренуф
8

Кроме того, while readсделайте петлю в рыбных раковинах (я предполагаю, что вы хотите рыбные раковины, учитывая, что вы использовали бирку).

command | while read line
    command $line
end

Несколько замечаний.

  • readне принимает -rаргументов и не интерпретирует обратную косую черту, чтобы облегчить наиболее распространенный вариант использования.
  • Вам не нужно заключать в кавычки $line, поскольку в отличие от bash, fish не разделяет переменные по пробелам.
  • commandсама по себе является синтаксической ошибкой (чтобы отловить такое использование аргументов-заполнителей). Замените его настоящей командой.
Конрад Боровски
источник
Не whileнужно в паре с do& doneвместо end?
АФ
@aff Это конкретно о рыбных раковинах, которые имеют другой синтаксис.
Конрад Боровски
Ах, вот что значит рыба.
АФ
6

Если вам нужно контролировать, где именно вводимый аргумент вставляется в вашу командную строку, или если вам нужно повторить его несколько раз, вам нужно использовать xargs -I{}.

ПРИМЕР № 1

Создайте пустую структуру папок, another_folderкоторая будет отражать подпапки в текущем каталоге:

    ls -1d ./*/ | xargs -I{} mkdir another_folder/{}
ПРИМЕР № 2

Примените операцию к списку файлов из stdin, в этом случае сделайте копию каждого .htmlфайла, добавив .bakрасширение:

    find . -iname "*.html" | xargs -I{} cp {} {}.bak

Со xargsстраницы руководства для MacOS / BSD :

 -I replstr
         Execute utility for each input line, replacing one or more occurrences of
         replstr in up to replacements (or 5 if no -R flag is specified) arguments
         to utility with the entire line of input.  The resulting arguments, after
         replacement is done, will not be allowed to grow beyond 255 bytes; this is
         implemented by concatenating as much of the argument containing replstr as
         possible, to the constructed arguments to utility, up to 255 bytes.  The
         255 byte limit does not apply to arguments to utility which do not contain
         replstr, and furthermore, no replacement will be done on utility itself.
         Implies -x.

xargsСтраница руководства по Linux :

   -I replace-str
          Replace  occurrences of replace-str in the initial-
          arguments with names read from standard input.  Al
          so,  unquoted  blanks do not terminate input items;
          instead the separator  is  the  newline  character.
          Implies -x and -L 1.
ccpizza
источник
1

Имея дело с потенциально неанизированными входами, я хотел бы видеть всю работу, "прописанную", построчно для визуального осмотра, прежде чем запускать ее (особенно когда это что-то разрушительное, например, очистка почтовых ящиков людей).

Поэтому я создаю список параметров (т. Е. Имена пользователей), передаю его в файл по одной записи на строку, например:

johndoe  
jamessmith  
janebrown  

Затем я открываю список vimи изменяю его с помощью выражений поиска и замены, пока не получу список полных команд, которые должны быть выполнены, например:

/bin/rm -fr /home/johndoe  
/bin/rm -fr /home/jamessmith 

Таким образом, если ваше регулярное выражение неполное, вы увидите, в какой команде будут возникать потенциальные проблемы (т.е. /bin/rm -fr johnnyo connor). Таким образом, вы можете отменить свое регулярное выражение и попробовать его снова с более надежной версией. Известно, что искажение имен известно, потому что трудно позаботиться обо всех крайних случаях, таких как Ван Гог, О'Коннорс, Сент-Клер, Смит-Вессон.

Наличие set hlsearchполезно для этого vim, так как оно подсвечивает все совпадения, так что вы можете легко определить, не совпадает ли оно или совпадает непреднамеренным образом.

После того, как ваше регулярное выражение станет идеальным, и оно поймает все случаи, для которых вы можете проверить / подумать, я обычно преобразую его в выражение sed, чтобы его можно было полностью автоматизировать для следующего запуска.

В тех случаях, когда количество строк ввода не позволяет выполнить визуальный осмотр, я настоятельно рекомендую выводить команду на экран (или, что еще лучше, в журнал) перед ее выполнением, поэтому, если она выдает ошибку, вы точно знаете, какая команда вызвала это потерпеть неудачу. Затем вы можете вернуться к исходному регулярному выражению и настроить еще раз.

Marcin
источник
0

Если программа игнорирует канал, но принимает файлы в качестве аргументов, тогда вы можете просто указать его на специальный файл /dev/stdin.

Я не знаком с Java, но вот пример того, как вы могли бы сделать это для Bash:

$ echo $'pwd \n cd / \n pwd' |bash /dev/stdin
/home/rolf
/

$ Необходим для перевода bash \nв новые строки. Я не уверен почему.

Рольф
источник
0

Я предпочитаю это - разрешить многострочные команды и очистить код

find -type f -name filenam-pattern* | while read -r F
do
  echo $F
  cat $F | grep 'some text'
done

ссылка https://stackoverflow.com/a/3891678/248616

Нам Г ВУ
источник
0

Здесь копировальную пасту вы можете сразу использовать:

cat list.txt | xargs -I{} command parameter {} parameter

Элемент из списка будет помещен туда, где {}, а остальная часть команды и параметров будет использоваться как есть.

DustWolf
источник