23

I have a string in the following format:

I'm\nNed\nNederlander
I'm\nLucky\nDay
I'm\nDusty\nBottoms

I would like to move this to an array of strings line by line such that:

$ echo "${ARRAY[0]}"
I'm\nNed\nNederlander

$ echo "${ARRAY[1]}"
I'm\nLucky\nDay

$ echo "${ARRAY[2]}"
I'm\nDusty\nBottoms

However, I'm running into problems with the "\n" characters within the string itself. They are represented in the string as two separate characters, the backslash and the 'n', but when I try to do the array split they get interpreted as newlines. Thus typical string splitting with IFS does not work.

For example:

$ read -a ARRAY <<< "$STRING"
$ echo "${#ARRAY[@]}"   # print number of elements
2

$ echo "${ARRAY[0]}"
I'mnNednNederla

$ echo "${ARRAY[1]}"
der
Richard de Wit
  • 7,102
  • 7
  • 44
  • 54
Cory Klein
  • 51,188
  • 43
  • 183
  • 243
  • BTW, does anybody know how to fix SE's terrible formatting of the code above? – Cory Klein Jul 31 '12 at 17:51
  • The "terrible formatting" is due to the apostrophes being interpreted as single quotes (which it expects to be balanced). Use the "block quote" tags instead of "code" tags. – twalberg Jul 31 '12 at 17:59

2 Answers2

38

By default, the read builtin allows \ to escape characters. To turn off this behavior, use the -r option. It is not often you will find a case where you do not want to use -r.

string="I'm\nNed\nNederlander
I'm\nLucky\nDay
I'm\nDusty\nBottoms"

arr=()
while read -r line; do
   arr+=("$line")
done <<< "$string"

In order to do this in one-line (like you were attempting with read -a), actually requires mapfile in bash v4 or higher:

mapfile -t arr <<< "$string"
jordanm
  • 33,009
  • 7
  • 61
  • 76
17

mapfile is more elegant, but it is possible to do this in one (ugly) line with read (useful if you're using a version of bash older than 4):

IFS=$'\n' read -d '' -r -a arr <<< "$string"
chepner
  • 497,756
  • 71
  • 530
  • 681