¿Cómo leer diferentes líneas de un archivo a diferentes variables?

14

Me gustaría leer diferentes líneas de un archivo de texto a diferentes variables. Por ejemplo

input.txt:

line1 foo foobar bar
line2 bar
line3 foo
line4 foobar bar

Quiero que este resultado se almacena en las variables var1, var2, var3y var4de tal manera que

var1=line1 foo foobar bar
var2=line2 bar

y así.

¿Podría alguien decirme cómo se hace? Traté de usar evalen un bucle for. No parece funcionar.

neet
fuente
3
Este es un mal enfoque. Ya que básicamente la única razón por la que las variables de ajuste es para hacer algo con ellos, y puesto que hay un gran número de herramientas específicamente diseñadas para hacer cosas con cada línea de texto en un archivo (awk, sed, grep, cut, et. Al.), es mucho mejor hacer lo que necesita hacer. No microgestión en un script de shell; orquestar herramientas para hacer el trabajo.
Comodín el

Respuestas:

20

Tu harías:

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

O:

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

(menos eficiente ya lineque rara vez está incorporado y la mayoría de los shells necesitan bifurcarse para implementar la sustitución de comandos. lineTampoco es un comando estándar).

Para usar un bucle:

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

O para definir automáticamente los nombres de las variables como line<++n>:

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

Tenga en cuenta que bashadmite variables de matriz y una función readarrayintegrada para leer líneas en una matriz:

readarray -t line < input.txt

Sin embargo , tenga en cuenta que, a diferencia de la mayoría de los otros shells, los bashíndices de matriz comienzan en 0, no en 1 (heredado de ksh), por lo que la primera línea estará en ${line[0]}, no ${line[1]}(aunque, como ha demostrado @Costas , puede hacer readarray(también conocido comomapfile ) comience a escribir los valores en el índice 1 (las bashmatrices también son contrarias a que la mayoría de las otras capas sean matrices dispersas ) con -O 1).

Ver también: ¿ Entender "IFS = read -r line"?

Stéphane Chazelas
fuente
12

Ofrecería usar una matriz para tales tareas

mapfile -t -O 1 var <input.txt

así que tendrás cada línea adentro ${var[1]}, ${var[2]}y así sucesivamente

Costas
fuente
Este one-liner funciona muy bien. Resolvió el problema por mí.
Matt Zabojnik
0

Esto no es estrictamente lo que solicitó, pero podría funcionar para sus necesidades. Puede serializar los datos con Awk. Tenga en cuenta que esto se romperá si su $ 1 no es un nombre de variable válido:

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

Resultado:

$ echo "$line1"
line1 foo foobar bar
Steven Penny
fuente
Este es un código realmente extraño! Parece que está utilizando awk para convertir todo el archivo de entrada en un script bash que luego obtiene. Tengo una vaga idea de lo que está haciendo quote (). Los nombres de variables más descriptivos ayudarían. ¿Podrías explicar las cosas un poco más?
Joe