Как прочитать разные строки файла для разных переменных?

14

Я хотел бы прочитать разные строки текстового файла для разных переменных. Например

input.txt:

line1 foo foobar bar
line2 bar
line3 foo
line4 foobar bar

Я хочу , чтобы этот результат будет храниться в переменных var1, var2, var3и var4такие , что

var1=line1 foo foobar bar
var2=line2 bar

и так далее.

Может кто-нибудь, пожалуйста, скажите мне, как это делается. Я пытался использовать evalв цикле. Это не похоже на работу.

нею
источник
3
Это плохой подход. Поскольку в основном единственной причиной для установки переменных является сделать что - то с ними, и так как существует огромное количество инструментов , специально разработанных , чтобы делать вещи с каждой строкой текста в файле (Awk, Sed, grep, cut, и др. И др.), гораздо лучше просто делать то, что нужно. Не занимайтесь микроуправлением в сценарии оболочки; оркестровать инструменты, чтобы сделать работу.
Wildcard

Ответы:

20

Вы бы сделали:

unset -v line1 line2
{ IFS= read -r line1 && IFS= read -r line2; } < input.txt

Или:

{ line1=$(line) && line2=$(line); } < input.txt

(менее эффективен, так как lineредко встроен, и большинству оболочек требуется форк для реализации подстановки команд. lineТакже больше не является стандартной командой).

Чтобы использовать цикл:

unset -v line1 line2 line3
for var in line1 line2 line3; do
  IFS= read -r "$var" || break
done < input.txt

Или для автоматического определения имен переменных как line<++n>:

n=1; while IFS= read -r "line$n"; do
  n=$((n + 1))
done < input.txt

Обратите внимание, что bashподдерживаются переменные массива и readarrayвстроенная функция для чтения строк в массив:

readarray -t line < input.txt

Однако обратите внимание, что в отличие от большинства других оболочек, bashиндексы массива начинаются с 0, а не с 1 (унаследовано от ksh), поэтому первая строка будет ${line[0]}, а не ${line[1]}(хотя как показала @Costas , вы можете readarray(иначе mapfile) начать записывать значения с индекса 1 ( bashмассивы также противоречат большинству других оболочек, являющихся разреженными массивами) с -O 1).

Смотрите также: Понять "IFS = read -r line"?

Стефан Шазелас
источник
12

Я бы предложил использовать массив для таких задач

mapfile -t -O 1 var <input.txt

так что вы будете иметь каждую строку ${var[1]}, ${var[2]}и так далее

Костас
источник
Этот однострочник работает очень хорошо. Решил проблему для меня.
Мэтт Забойник
0

Это не совсем то, что вы просили, но это может работать для ваших нужд. Вы можете сериализовать данные с помощью Awk. Обратите внимание, что это сломается, если ваш $ 1 не является допустимым именем переменной:

awk '
function quote(str,   d, m, x, y, z) {
  d = "\47"; m = split(str, x, d)
  for (y in x) z = z d x[y] (y < m ? d "\\" d : d)
  return z
}
{
  print $1 "=" quote($0)
}
' input.txt > /tmp/input.sh
. /tmp/input.sh

Результат:

$ echo "$line1"
line1 foo foobar bar
Стивен Пенни
источник
Это серьезно странный код! Похоже, что вы используете awk, чтобы превратить весь входной файл в скрипт bash, который вы затем используете. У меня есть смутное представление о том, что делает quote (). Более описательные имена переменных помогут. Не могли бы вы объяснить вещи немного больше?
Джо