it-swarm-ru.tech

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

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

176
Xodarap

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

... | xargs command
98
Keith

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

cat file... | xargs -n1 command

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

<file xargs -n1 command
193
Michael Goldshteyn

В 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
117
Steven D

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

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

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

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

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

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

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

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

22
Johan

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

cat stuff | grep pattern | parallel Java MyProg

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

15
Ole Tange

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

command | while read line
    command $line
end

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

  • read не берет -r, и он не интерпретирует обратную косую черту, чтобы облегчить наиболее распространенный вариант использования.
  • Вам не нужно цитировать $line, в отличие от bash, fish не разделяет переменные по пробелам.
  • command само по себе является синтаксической ошибкой (чтобы отловить такое использование аргументов-заполнителей). Замените его настоящей командой.
10
Konrad Borowski

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

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

johndoe  
jamessmith  
janebrown  

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

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

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

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

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

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

1
Marcin

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

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

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

1
DustWolf

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

  1. Один раз: используйте -n 1, это гарантирует, что команда вызывается ровно один раз для каждого аргумента. Однако по умолчанию xargs предполагает, что аргументы разделены пробелами - это приведет к разрыву, когда файлы будут содержать пробелы.
  2. В строке: используйте -d '\n' или предварительно обработайте ввод с помощью tr '\n' '\0' и ​​используйте -0. Это делает команду устойчивой к пробелам на входе.

Окончательная командная строка становится:

.... | xargs -n 1 -d '\n' <command>

или (с tr)

.... | tr '\n' '\0' | xargs -n 1 -0 <command>

Если ваша команда может обрабатывать несколько аргументов одновременно (например, grep или sed), вы можете опустить -n 1 для ускорения работы во многих случаях.

0
krlmlr

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

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

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

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

0
Rolf

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

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

ref https://stackoverflow.com/a/3891678/248616

0
Nam G VU