49

In Java, if you know for certain a file is very small, you can use readBytes() method to read the content in one go instead of read it line by line or using buffer.

Just wondering in shell script, I know we can do something like:

 while read line do echo $line LINE = $line done < "test.file" echo $LINE 

If my test.file is like:

testline1 testline2 testline3 

This only gives me the last line to $LINE. $LINE contains "testline3".

My question is: How can I read the whole file with multiple lines into one single variable,so I can get $LINE="testline1\ntestline2\ntestline3"?

4
  • 5
    DATA=$(cat file), but why would you want to? Just read the file whenever you need the content! Commented Jun 11, 2012 at 17:01
  • 1
    Two things not directly related to your question: first, LINE = $line doesn't actually work; the space around the equals sign renders it invalid. Second, if you're manipulating text that you want to preserve literally, you should set IFS to null for your read calls (otherwise leading whitespace on each line will be nuked) and pass the -r option to read and the -E option to echo (otherwise, any backslashes in your file will mess things up). See my answer below for example. Commented Jun 11, 2012 at 18:21
  • @MarkReed: I never have understood the purpose of -E since that's Bash's default behavior for echo. Commented Jun 11, 2012 at 19:22
  • 2
    possible duplicate of read a file and save it in variable using shell script Commented Mar 24, 2014 at 8:57

3 Answers 3

132

Process the lines inside the loop instead of after it. If you really need the file in a variable:

var=$(<file) 
Sign up to request clarification or add additional context in comments.

2 Comments

Could you also explain what's happening here?
@sbhatla: The variable is set to the value of the command substitution which contains a redirect which reads the contents of the file. It's similar to var=$(cat file) but it doesn't require spawning an external executable).
13

Another alternative is to use the nice mapfile builtin:

mapfile < test.file echo "${MAPFILE[@]}" 

3 Comments

There is no man page for it, but help mapfile will give some more information. The utility will by default put the first line into entry 0, the second line into entry 1, and so on. You likely also want to use it with the -t option (strip newlines).
Note that mapfile was introduced in bash 4, so is unavailable in /bin/bash on OS X. Which is probably the last bastion of bash 3 likely to be encountered in the wild, but also not likely to be updated due to licensing concerns with Apple and the new GPL.
When Apple updated from Mac OS to Macos, bash 4.4 was included.
12

As another option, you can build an array of lines. If you're running bash 4+, you can use the mapfile builtin:

mapfile -t lines <test.file 

If you want the lines to be output as well as stored you could do something like this:

mapfile -t lines < <(tee /dev/tty <test.file) 

Then "${lines[0]}" will be the first line of the file, "${lines[1]}" the second, and so on. ${#lines[@]} will be the number of lines; "${lines[@]}" will be the whole array, while "${lines[*]}" will be the lines joined together with spaces into one big string.

For older versions of bash, you can build the array manually:

lines=() while IFS= read -r line do printf '%s\n' "$line" lines+=("$line") done < test.file 

2 Comments

This would be easier written as LINES+=("$line"), wouldn't it?
Yup. I've learned a bit in 4 years, it seems. Good to know. :)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.