How to read a file into a variable in shell? How to read a file into a variable in shell? unix unix

How to read a file into a variable in shell?


In cross-platform, lowest-common-denominator sh you use:

#!/bin/shvalue=`cat config.txt`echo "$value"

In bash or zsh, to read a whole file into a variable without invoking cat:

#!/bin/bashvalue=$(<config.txt)echo "$value"

Invoking cat in bash or zsh to slurp a file would be considered a Useless Use of Cat.

Note that it is not necessary to quote the command substitution to preserve newlines.

See: Bash Hacker's Wiki - Command substitution - Specialities.


If you want to read the whole file into a variable:

#!/bin/bashvalue=`cat sources.xml`echo $value

If you want to read it line-by-line:

while read line; do        echo $line    done < file.txt


Two important pitfalls

which were ignored by other answers so far:

  1. Trailing newline removal from command expansion
  2. NUL character removal

Trailing newline removal from command expansion

This is a problem for the:

value="$(cat config.txt)"

type solutions, but not for read based solutions.

Command expansion removes trailing newlines:

S="$(printf "a\n")"printf "$S" | od -tx1

Outputs:

0000000 610000001

This breaks the naive method of reading from files:

FILE="$(mktemp)"printf "a\n\n" > "$FILE"S="$(<"$FILE")"printf "$S" | od -tx1rm "$FILE"

POSIX workaround: append an extra char to the command expansion and remove it later:

S="$(cat $FILE; printf a)"S="${S%a}"printf "$S" | od -tx1

Outputs:

0000000 61 0a 0a0000003

Almost POSIX workaround: ASCII encode. See below.

NUL character removal

There is no sane Bash way to store NUL characters in variables.

This affects both expansion and read solutions, and I don't know any good workaround for it.

Example:

printf "a\0b" | od -tx1S="$(printf "a\0b")"printf "$S" | od -tx1

Outputs:

0000000 61 00 6200000030000000 61 620000002

Ha, our NUL is gone!

Workarounds:

  • ASCII encode. See below.

  • use bash extension $"" literals:

    S=$"a\0b"printf "$S" | od -tx1

    Only works for literals, so not useful for reading from files.

Workaround for the pitfalls

Store an uuencode base64 encoded version of the file in the variable, and decode before every usage:

FILE="$(mktemp)"printf "a\0\n" > "$FILE"S="$(uuencode -m "$FILE" /dev/stdout)"uudecode -o /dev/stdout <(printf "$S") | od -tx1rm "$FILE"

Output:

0000000 61 00 0a0000003

uuencode and udecode are POSIX 7 but not in Ubuntu 12.04 by default (sharutils package)... I don't see a POSIX 7 alternative for the bash process <() substitution extension except writing to another file...

Of course, this is slow and inconvenient, so I guess the real answer is: don't use Bash if the input file may contain NUL characters.