Як передати кожен рядок текстового файлу як аргумент команді?


42

Я шукаю написати сценарій, який приймає .txtім'я файлу як аргумент, читає файл рядок за рядком і передає кожен рядок команді. Наприклад, він запускається command --option "LINE 1", тоді command --option "LINE 2"і т. Д. Виведення команди записується в інший файл. Як мені це робити? Я не знаю, з чого почати.

Відповіді:


26

Використовуйте while readцикл:

: > another_file  ## Truncate file.

while IFS= read -r LINE; do
    command --option "$LINE" >> another_file
done < file

Іншим є перенаправлення виводу за блоком:

while IFS= read -r LINE; do
    command --option "$LINE"
done < file > another_file

Останнє - відкрити файл:

exec 4> another_file

while IFS= read -r LINE; do
    command --option "$LINE" >&4
    echo xyz  ## Another optional command that sends output to stdout.
done < file

Якщо одна з команд читає введення, було б непогано використовувати інший fd для введення, щоб команди не їли його (тут припускаємо ksh, zshабо bashдля цього -u 3використовуємо <&3замість нього портативно):

while IFS= read -ru 3 LINE; do
    ...
done 3< file

Нарешті, щоб прийняти аргументи, ви можете зробити:

#!/bin/bash

FILE=$1
ANOTHER_FILE=$2

exec 4> "$ANOTHER_FILE"

while IFS= read -ru 3 LINE; do
    command --option "$LINE" >&4
done 3< "$FILE"

Який із них може працювати як:

bash script.sh file another_file

Додаткова ідея. З bash, використовуйте readarray:

readarray -t LINES < "$FILE"

for LINE in "${LINES[@]}"; do
    ...
done

Примітка. Ви IFS=можете опустити, якщо ви не заперечуєте, щоб значення рядків були обрізані провідними та кінцевими пробілами.


28

Інший варіант є xargs.

З GNU xargs:

< file xargs -I{} -d'\n' command --option {} other args

{} є місцем місця для рядка тексту.

Інших xargsнемає -d, але деякі мають -0введення з обмеженням NUL. З цим можна зробити:

< file tr '\n' '\0' | xargs -0 -I{} command --option {} other args

У системах, сумісних з Unix ( -Iнеобов'язково в POSIX і потрібен лише для систем, сумісних з Unix), вам потрібно буде попередньо обробити введення для цитування рядків у форматі, очікуваному xargs:

< file sed 's/"/"\\""/g;s/.*/"&"/' |
  xargs -E '' -I{} command --option {} other args

Однак зауважте, що деякі xargsреалізації мають дуже низький ліміт максимального розміру аргументу (255, наприклад, для Solaris, мінімально дозволений специфікацією Unix).


зводиться до<file xargs -L 1 -I{} command --option {} other args
iruvar

@iruvar, ні, це обробить цитати та видалить деякі пробіли.
Стефан Шазелас

7

Тримаючись точно до питання:

#!/bin/bash

# xargs -n param sets how many lines from the input to send to the command

# Call command once per line
[[ -f $1 ]] && cat $1 | xargs -n1 command --option

# Call command with 2 lines as args, such as an openvpn password file
# [[ -f $1 ]] && cat $1 | xargs -n2 command --option

# Call command with all lines as args
# [[ -f $1 ]] && cat $1 | xargs command --option

1
працював як шарм :-)
BugHunter

3

Найкраща відповідь, яку я знайшов:

for i in `cat`; do "$cmd" "$i"; done < $file

Редагувати:

... через чотири роки ...

після кількох голосів "за" і трохи більше досвіду я рекомендую зараз наступне

xargs -l COMMAND < file

3
Це буде викликати команду один раз для кожного слова у файлі. Крім того, ви завжди повинні цитувати посилання на змінні оболонки (як у do "$cmd" "$i";), якщо у вас немає причин цього не робити; якщо файл містив *слово як сам по собі, запускався б ваш код $cmd *, який, звичайно, запускав би команду зі списком файлів у поточному каталозі.
G-Man каже: "Відновіть Моніку"

1
@ G-Man, окрім in zsh, `cat`би вже розширив *(без котировки $iвсе-таки можна розширити деякий підстановку (другий раунд), якщо розширення `cat`вводить деякі). У будь-якому випадку, такий підхід дійсно неправильний.
Стефан Шазелас

1
+1. Це буде добре, якщо кожен рядок містить лише вхід, необхідний для використання команди без пробілів.
Субін Себастьян

Це діє на кожне слово, чи є варіант цього, який діє на кожен рядок?
thebunnyrules

Питання полягало в обробці списку імен файлів по рядку.
Steffen Roller

2
    sed "s/'/'\\\\''/g;s/.*/\$* '&'/" <<\FILE |\
    sh -s -- command echo --option
all of the{&}se li$n\es 'are safely shell
quoted and handed to command as its last argument
following --option, and, here, before that echo
FILE

ВИХІД

--option all of the{&}se li$n\es 'are safely shell
--option quoted and handed to command as its last argument
--option following --option, and, here, before that echo

-2
ed file.txt
%g/^/s// /
2,$g/^/-,.j
1s/^/command/
wq
chmod 755 file.txt
./file.txt

Візьміть всі рядки файлу і передайте їх як аргументи до однієї команди, тобто,

command line1 line2 line3 ....

Якщо вам потрібен --optionпрапор, щоб передувати кожному рядку, змініть другу команду на:

%g/^/s// --option /

1
Що -1 для використання ед ... насправді?
користувач2217522

3
Я вас не порушив, але людина, яка, мабуть, мала ці причини: (1) Це не робить те, про що запитує. Це викликає команду один раз із усім вмістом файлу в командному рядку; а не один раз на рядок . (2) Це не робить нічого для обробки символів, які є спеціальними для оболонки (які можуть бути у файлі); наприклад, ', ", <, >, ;і т.д. (3) Це створює непотрібний тимчасовий файл. (4) Такі речі, як правило, робляться з документами "тут". (5) Ваші edкоманди незграбні; перші дві команди можна зменшити до %s/^/ /та %j.
G-Man каже: "Відновіть Моніку"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.