Jak czytać różne wiersze pliku dla różnych zmiennych?

14

Chciałbym przeczytać różne wiersze pliku tekstowego dla różnych zmiennych. Na przykład

input.txt:

line1 foo foobar bar
line2 bar
line3 foo
line4 foobar bar

Chcę wynik ten powinien być przechowywany w zmiennych var1, var2, var3i var4takie, które

var1=line1 foo foobar bar
var2=line2 bar

i tak dalej.

Czy ktoś mógłby mi powiedzieć, jak to się robi. Próbowałem użyć evalw pętli for. Wydaje się, że to nie działa.

neet
źródło
3
To jest złe podejście. Ponieważ w zasadzie jedynym powodem do zmiennych ustawionych jest coś z nimi zrobić, a ponieważ istnieje ogromna liczba narzędzi specjalnie zaprojektowanych do robienia rzeczy z każdego wiersza tekstu w pliku (awk, sed grep, cutet. Al.), o wiele lepiej jest robić to, co trzeba. Nie wykonuj mikromanagowania w skrypcie powłoki; koordynuj narzędzia, aby wykonać zadanie.
Wildcard

Odpowiedzi:

20

Zrobiłbyś:

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

Lub:

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

(mniej wydajne, ponieważ linerzadko jest wbudowane, a większość powłok wymaga rozwidlenia w celu zaimplementowania zastępowania poleceń. nie linejest też już standardowym poleceniem).

Aby użyć pętli:

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

Lub, aby automatycznie zdefiniować nazwy zmiennych jako line<++n>:

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

Zauważ, że bashobsługuje zmienne tablicowe i readarraywbudowane do odczytu linii w tablicy:

readarray -t line < input.txt

Zauważ jednak, że w przeciwieństwie do większości innych powłok, bashindeksy tablic zaczynają się od 0, a nie 1 (dziedziczone z ksh), więc pierwszy wiersz będzie ${line[0]}, a nie ${line[1]}(chociaż jak pokazał @Costas , możesz readarray(aka mapfile) zacząć zapisywać wartości w indeksie 1 ( bashtablice również w przeciwieństwie do większości innych powłok są rzadkimi tablicami) z -O 1).

Zobacz także: Zrozumieć „IFS = read -r line”?

Stéphane Chazelas
źródło
12

Proponuję użyć tablicy do takich zadań

mapfile -t -O 1 var <input.txt

więc musisz każdą linię ${var[1]}, ${var[2]}i tak dalej

Costas
źródło
Ten jednowarstwowy działa bardzo dobrze. Rozwiązałem problem dla mnie.
Matt Zabojnik
0

Nie jest to dokładnie to, o co prosiłeś, ale może działać dla twoich potrzeb. Możesz serializować dane za pomocą Awk. Zauważ, że to się zepsuje, jeśli 1 $ nie jest prawidłową nazwą zmiennej:

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

Wynik:

$ echo "$line1"
line1 foo foobar bar
Steven Penny
źródło
To jest naprawdę dziwny kod! Wygląda na to, że używasz awk, aby przekształcić cały plik wejściowy w skrypt bash, który następnie pobierasz. Mam niejasne pojęcie o tym, co robi quote (). Pomocne byłyby bardziej opisowe nazwy zmiennych. Czy możesz wyjaśnić coś więcej?
Joe