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


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.