121

I found this.

And I am trying this:

x='some thing' y=(${x//\n/}) 

And I had no luck, I thought it could work with double backslash:

y=(${x//\\n/}) 

But it did not.

To test I am not getting what I want I am doing:

echo ${y[1]} 

Getting:

some thing 

Which I want to be:

some 

I want y to be an array [some, thing]. How can I do this?

3
  • 4
    Note that some would be in ${y[0]}, not ${y[1]}, once you solve the newline issue. Commented Nov 4, 2013 at 16:46
  • 1
    @juanpastas FYI the linked to solution has been updated to include how to split on a newline character when your input has spaces. stackoverflow.com/a/5257398/52074 Commented Oct 3, 2018 at 17:30
  • Related: Echo newline in Bash prints literal \n Commented Dec 7, 2024 at 14:49

3 Answers 3

191

Another way:

x=$'Some\nstring' readarray -t y <<<"$x" 

Or, if you don't have bash 4, the bash 3.2 equivalent:

IFS=$'\n' read -rd '' -a y <<<"$x" 

You can also do it the way you were initially trying to use:

y=(${x//$'\n'/ }) 

This, however, will not function correctly if your string already contains spaces, such as 'line 1\nline 2'. To make it work, you need to restrict the word separator before parsing it:

IFS=$'\n' y=(${x//$'\n'/ }) 

...and then, since you are changing the separator, you don't need to convert the \n to space anymore, so you can simplify it to:

IFS=$'\n' y=($x) 

This approach will function unless $x contains a matching globbing pattern (such as "*") - in which case it will be replaced by the matched file name(s). The read/readarray methods require newer bash versions, but work in all cases.

Sign up to request clarification or add additional context in comments.

9 Comments

Matching instances of \n with \\n in the expansion pattern doesn't work; instead, you have to splice in a \n literal: echo ${x//$'\n'/ }.
The readarray approach is actually the most robust one, because ($x) is still subject to globbing: if any line happens to be a valid, single-token globbing pattern (e.g., *), it WILL be expanded, if files happen to match. readarray requires bash 4, however; here's the bash 3.2 equivalent (for those on OS X): IFS=$'\n' read -rd '' -a y <<<"$x". Since your post is gaining traction, would you mind adding these findings?
In OS-X/Macland, you have to use bash 3.2 (or at least without updating BASH). Thus the mysterious read -rd ' ' must be used (and works!) the online manual page I found is pretty cryptic about this (ss64.com/bash/read.html)...it's pretty mind-bending...does it mean "turn off \n, and then use emptiness as the delimiter?"
Please remove the horrible IFS=$'\n' y=($x) parts from your answer! it's broken when $x contains globs (like *, [...], etc.) — I know it can be “fixed” with set -f, but at this point we're fighting against the shell. The semantically correct ways (and bugfree ways) are the readarray/mapfile way and the read way.
Also, note that the return code of your read command is 1 (e.g., a failure), since the delimiter is not seen at the end of the stream read. The proper way is: IFS=$'\n' read -d '' -ra y < <(printf '%s;\0' "$x"), as mentioned in this answer. Note that in the case of a space-like character (i.e., space, newline or tab character), consecutive delimiters will be considered as a unique one. So the read method is subtly different from the readarray/mapfile method for newlines.
|
9

There is another way if all you want is the text up to the first line feed:

x='some thing' y=${x%$'\n'*} 

After that y will contain some and nothing else (no line feed).

What is happening here?

We perform a parameter expansion substring removal (${PARAMETER%PATTERN}) for the shortest match up to the first ANSI C line feed ($'\n') and drop everything that follows (*).

Comments

0

Using sed:

newStr=`sed ":a;N;s/\n/@@@/g" <<< "$variable"` 

will replace \n by @@@, and to split correctly, then this use before loop:

IFS='@@@' for a in $newStr do ... 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.