Виконати команду один раз на рядок вхідного каналу?


162

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

Відповіді:


91

Ось що і 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 echo whee розпадається на слова, а не на рядки
Gismo Ranas

112

У Bash або будь-якій іншій оболонці в стилі Борна (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варіанті зробив його роботу

Це також працює, коли кожен рядок потрібно надсилати до stdin:command | while read -r line; do echo "$line" | command ; done
День

21

Я погоджуюся з Кітом, xargs - це найзагальніший інструмент для роботи.

Зазвичай я використовую підхід в 3 кроки.

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

Існують менші та швидші способи, але цей спосіб майже завжди працює.

Простий приклад:

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

2 перші рядки вибирають деякі файли для роботи, потім awk готує хороший рядок із командою для виконання та деякими аргументами та $ 1 - це перший вхід стовпця з труби. І, нарешті, я переконуюсь, що xargs посилає цей рядок на бешкет, який просто виконує його.

Це трохи надмірно, але цей рецепт мені допоміг у багатьох місцях, оскільки він дуже гнучкий.


6
Зауважте, xargs -0використовує нульовий байт як роздільник записів, тому ваша заява про awk print має бутиprintf("MyJavaProg --args \"%s\"\0",$1)
glenn jackman

@glenn: Пропущений нульовий знак, відповідь оновиться
Йохан

@Johan не велика справа, але якщо ви використовуєте, awkви можете це зробити, щоб відповідати шаблону і пропустити, grep наприклад,ls | awk '/xls/ {print...
Ерік Renouf

15

GNU Paralellel створений для таких завдань. Найпростіше використання:

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, то влада вам
Ерік Реноф

8

Крім того, while readпетлю в рибній оболонці (я припускаю, що ви хочете, щоб ви використовували оболонку, вважаючи, що ви використовували тег).

command | while read line
    command $line
end

Кілька пунктів до уваги.

  • readне приймає -rаргументи, і він не інтерпретує ваші зворотні риси, щоб полегшити більшість випадків використання.
  • Цитувати не потрібно $line, оскільки на відміну від bash, риба не розділяє змінні пробілами.
  • 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.

1

Маючи справу з потенційно несанітованими входами, мені подобається бачити, що вся робота «прописана» рядком за візуальним оглядом, перш ніж я запускаю її (особливо коли це щось руйнівне, як чистка поштової скриньки людей).

Тож, що я роблю, це генерувати список параметрів (тобто імена користувачів), подавати його у файл у одному записі на рядок, як це:

johndoe  
jamessmith  
janebrown  

Потім я відкриваю список у vimі обробляю його з пошуковими і замінюючими виразами, поки не отримаю список повних команд, які потрібно виконати, як-от так:

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

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

Це set hlsearchє корисним для цього vim, оскільки це виділить усі матчі, тому ви зможете легко помітити, якщо він не збігається, або збіг ненавмисно.

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

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


0

Якщо програма ігнорує трубу, але приймає файли як аргументи, ви можете просто вказати її на спеціальний файл /dev/stdin.

Я не знайомий з java, але ось приклад того, як ви зробили це для bash:

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

$ Необхідний для переведення bash \nв нові рядки. Я не впевнений, чому.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.