Você faria:
unset -v line1 line2
{ IFS= read -r line1 && IFS= read -r line2; } < input.txt
Ou:
{ line1=$(line) && line2=$(line); } < input.txt
(menos eficiente, pois line raramente é embutido e a maioria das shells precisam ser forçadas para implementar a substituição de comandos. line também não é mais um comando padrão).
Para usar um loop:
unset -v line1 line2 line3
for var in line1 line2 line3; do
IFS= read -r "$var" || break
done < input.txt
Ou para definir automaticamente os nomes das variáveis como line<++n> :
n=1; while IFS= read -r "line$n"; do
n=$((n + 1))
done < input.txt
Observe que bash suporta variáveis de matriz e readarray incorporado para ler linhas em uma matriz:
readarray -t line < input.txt
Observe, entretanto, que ao contrário da maioria dos outros shells, bash array indices começam em 0 não 1 (herdado de ksh ), então a primeira linha estará em ${line[0]} , não ${line[1]} (embora como @Costas mostrou , você pode fazer com que readarray (aka mapfile ) comece a escrever os valores no indice 1 ( bash arrays também ao contrário da maioria dos outros shells sendo Matrizes esparsas ) com -O 1 ).
Veja também: Entenda "IFS = read -r line"?