14

I would like to read different lines of a text file to different variables. For example

input.txt:

line1 foo foobar bar
line2 bar
line3 foo
line4 foobar bar

I want this result to be stored in variables var1, var2, var3 and var4 such that

var1=line1 foo foobar bar
var2=line2 bar

and so on.

Could someone please tell me how it is done. I tried to use eval in a for loop. It doesn't seem to work.

  • 3
    This is a bad approach. Since basically the only reason to set variables is to do something with them, and since there are a vast number of tools specifically designed to do things with every line of text in a file (Awk, Sed, grep, cut, et. al.), it's much much better to just do what you need to do. Don't micromanage in a shell script; orchestrate tools to get the job done. – Wildcard Jan 26 '17 at 1:25
20

You'd do:

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

Or:

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

(less efficient as line is rarely built-in and most shells need to fork to implement command substitution. line is also no longer a standard command).

To use a loop:

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

Or to automatically define the names of the variables as line<++n>:

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

Note that bash supports array variables and a readarray builtin to read lines into an array:

readarray -t line < input.txt

Note however that contrary to most other shells, bash array indices start at 0 not 1 (inherited from ksh), so the first line will be in ${line[0]}, not ${line[1]} (though as @Costas has shown, you can make readarray (aka mapfile) start writing the values at indice 1 (bash arrays also contrary to most other shells' being sparse arrays) with -O 1).

See also: Understand "IFS= read -r line"?

11

I'd offer to use array for such task(s)

mapfile -t -O 1 var <input.txt

so you'll have each line in ${var[1]}, ${var[2]} and so on

0

This is not strictly what you asked for, but it might work for your needs. You can serialize the data with Awk. Note that this will break if your $1 is not a valid variable name:

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

Result:

$ echo "$line1"
line1 foo foobar bar
  • This is seriously strange code! It appears that you're using awk to turn the whole input file into a bash script which you then source. I have a vague idea of what quote() is doing. More descriptive variable names would help. Could you explain things a bit more? – Joe Jan 28 '17 at 6:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.