Reputation: 13981
I want to write some pre-defined texts to a file with the following:
text="this is line one\n
this is line two\n
this is line three"
echo -e $text > filename
I'm expecting something like this:
this is line one
this is line two
this is line three
But got this:
this is line one
this is line two
this is line three
I'm positive that there is no space after each \n
, but how does the extra space come out?
Upvotes: 732
Views: 961287
Reputation: 6125
If you're trying to get the string into a variable, another easy way is something like this:
USAGE=$(cat <<-END
This is line one.
This is line two.
This is line three.
END
)
If you indent your string with tabs (i.e., '\t'), the indentation will be stripped out. If you indent with spaces, the indentation will be left in.
NOTES:
END
text must appear on a line by itself.\t
, \n
) you must quote it. I.e. use echo "$USAGE"
(not: echo $USAGE
)Upvotes: 392
Reputation:
In bash
and dash
(the ones I tested, should work for yours), you can use some Python-style triple-quote strings:
echo """Hello
World!"""
This outputs:
Hello
World!
Upvotes: 1
Reputation: 5485
As per @alphado's answer, and subsequent comments, this is what worked for me and gives 0 exit status.
read -r -d '\0' VAR <<- EOM
This is line 1.
This is line 2.
Line 3.
\0
EOM
<<-
to ignore leading tabs\0
as explicit "end marker"\$
to print verbatim.(Just putting this as a separate answer because it got hard to decipher this between all the comments, and did not want to change too much in original answer)
Upvotes: 3
Reputation: 52449
This looks way too bash-like :) (hard to read) for my general taste:
cat << EndOfMessage
This is line 1.
This is line 2.
Line 3.
EndOfMessage
Let's get something a little more Pythonic (this is still bash):
text="this is line one
this is line two
this is line three\n"
dedent text
printf "$text" # print to screen
printf "$text" > file.txt # print to a file
Ah...that's better. :) It reminds me of Python's textwrap.dedent()
function which I use here.
Here is what the magic dedent
function looks like:
dedent() {
local -n reference="$1"
reference="$(echo "$reference" | sed 's/^[[:space:]]*//')"
}
Sample output to screen:
this is line one
this is line two
this is line three
WithOUT calling dedent text
first`, the output would look like this:
this is line one
this is line two
this is line three
The variable text
is passed to dedent
by reference, so that what is modified inside the function affects the variable outside the function.
For more details and explanation and references, see my other answer here: Equivalent of python's textwrap dedent in bash
OP's quote (with my emphasis added):
I'm positive that there is no space after each
\n
, but how does the extra space come out?
Your original attempt was this:
text="this is line one\n
this is line two\n
this is line three"
echo -e $text
...but your output had an extra space before the 2nd and 3rd line. Why?
By deduction and experimentation, my conclusion is that echo
converts the actual newline at the end of the line (the one you got when you actually pressed Enter) into a space. That space therefore shows up before the line just after each \n
in the text.
So, the solution is to escape the real newline at the end of each line by putting a backslash \
at the end of any line within the quotes of your string, like this:
text="this is line one\n\
this is line two\n\
this is line three"
echo -e "$text"
Do NOT put a space before those trailing backslashes (like this: text="this is line one\n \
) or that space will go right back into your output and cause you the same problem with the extra space!
OR, just use my technique with the dedent
function above, which also has the added functionality of being able to indent with your code to look really pretty and nice and readable.
Upvotes: 13
Reputation: 111
Or keeping text indented with whitespaces:
#!/bin/sh
sed 's/^[[:blank:]]*//' >filename <<EOF
this is line one
this is line two
this is line three
EOF
Same but using a varible:
#!/bin/sh
text="$(sed 's/^[[:blank:]]*//' << whatever
this is line one
this is line two
this is line three
)"
echo "$text" > filename
;-)
Upvotes: 1
Reputation: 8446
I came hear looking for this answer but also wanted to pipe it to another command. The given answer is correct but if anyone wants to pipe it, you need to pipe it before the multi-line string like this
echo | tee /tmp/pipetest << EndOfMessage
This is line 1.
This is line 2.
Line 3.
EndOfMessage
This will allow you to have a multi line string but also put it in the stdin of a subsequent command.
Upvotes: 7
Reputation: 390
There are many ways to do it. For me, piping the indented string into sed works nicely.
printf_strip_indent() {
printf "%s" "$1" | sed "s/^\s*//g"
}
printf_strip_indent "this is line one
this is line two
this is line three" > "file.txt"
This answer was based on Mateusz Piotrowski's answer but refined a bit.
Upvotes: 1
Reputation: 11169
Heredoc sounds more convenient for this purpose. It is used to send multiple commands to a command interpreter program like ex or cat
cat << EndOfMessage
This is line 1.
This is line 2.
Line 3.
EndOfMessage
The string after <<
indicates where to stop.
To send these lines to a file, use:
cat > $FILE <<- EOM
Line 1.
Line 2.
EOM
You could also store these lines to a variable:
read -r -d '' VAR << EOM
This is line 1.
This is line 2.
Line 3.
EOM
This stores the lines to the variable named VAR
.
When printing, remember the quotes around the variable otherwise you won't see the newline characters.
echo "$VAR"
Even better, you can use indentation to make it stand out more in your code. This time just add a -
after <<
to stop the tabs from appearing.
read -r -d '' VAR <<- EOM
This is line 1.
This is line 2.
Line 3.
EOM
But then you must use tabs, not spaces, for indentation in your code.
Upvotes: 1062
Reputation: 2672
Just to mention a simple one-line concatenation as it can be useful sometimes.
# for bash
v=" guga "$'\n'" puga "
# Just for an example.
v2="bar "$'\n'" foo "$'\n'"$v"
# Let's simplify the previous version of $v2.
n=$'\n'
v3="bar ${n} foo ${n}$v"
echo "$v3"
You'll get something like this
bar foo guga puga
All leading and ending white spaces will be preserved right for
echo "$v3" > filename
Upvotes: 0
Reputation: 37712
in a bash script the following works:
#!/bin/sh
text="this is line one\nthis is line two\nthis is line three"
echo -e $text > filename
alternatively:
text="this is line one
this is line two
this is line three"
echo "$text" > filename
cat filename gives:
this is line one
this is line two
this is line three
Upvotes: 76
Reputation: 9117
I've found more solutions since I wanted to have every line properly indented:
You may use echo
:
echo "this is line one" \
"\n""this is line two" \
"\n""this is line three" \
> filename
It does not work if you put "\n"
just before \
on the end of a line.
Alternatively, you can use printf
for better portability (I happened to have a lot of problems with echo
):
printf '%s\n' \
"this is line one" \
"this is line two" \
"this is line three" \
> filename
Yet another solution might be:
text=''
text="${text}this is line one\n"
text="${text}this is line two\n"
text="${text}this is line three\n"
printf "%b" "$text" > filename
or
text=''
text+="this is line one\n"
text+="this is line two\n"
text+="this is line three\n"
printf "%b" "$text" > filename
Another solution is achieved by mixing printf
and sed
.
if something
then
printf '%s' '
this is line one
this is line two
this is line three
' | sed '1d;$d;s/^ //g'
fi
It is not easy to refactor code formatted like this as you hardcode the indentation level into the code.
It is possible to use a helper function and some variable substitution tricks:
unset text
_() { text="${text}${text+
}${*}"; }
# That's an empty line which demonstrates the reasoning behind
# the usage of "+" instead of ":+" in the variable substitution
# above.
_ ""
_ "this is line one"
_ "this is line two"
_ "this is line three"
unset -f _
printf '%s' "$text"
Upvotes: 50
Reputation: 37
it will work if you put it as below:
AA='first line
\nsecond line
\nthird line'
echo $AA
output:
first line
second line
third line
Upvotes: 2
Reputation: 3342
The following is my preferred way to assign a multi-line string to a variable (I think it looks nice).
read -r -d '' my_variable << \
_______________________________________________________________________________
String1
String2
String3
...
StringN
_______________________________________________________________________________
The number of underscores is the same (here 80) in both cases.
Upvotes: 17
Reputation: 11786
echo
adds spaces between the arguments passed to it. $text
is subject to variable expansion and word splitting, so your echo
command is equivalent to:
echo -e "this" "is" "line" "one\n" "this" "is" "line" "two\n" ...
You can see that a space will be added before "this". You can either remove the newline characters, and quote $text
to preserve the newlines:
text="this is line one
this is line two
this is line three"
echo "$text" > filename
Or you could use printf
, which is more robust and portable than echo
:
printf "%s\n" "this is line one" "this is line two" "this is line three" > filename
In bash
, which supports brace expansion, you could even do:
printf "%s\n" "this is line "{one,two,three} > filename
Upvotes: 137