Как передать каждую строку текстового файла в качестве аргумента в команду?


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";), если у вас нет причин не делать этого; если файл *сам по себе содержит слово a, ваш код будет выполняться $cmd *, что, конечно же, приведет к выполнению команды со списком файлов в текущем каталоге.
G-Man говорит: «Восстановите Монику»

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

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

Это действует на каждое слово, есть ли вариант этого, который действует на каждую строку?
thebunnyrules

Вопрос заключался в том, чтобы построчно обрабатывать список имен файлов.
Штеффен Роллер

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 для использования ed ... на самом деле?
user2217522

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