Multi-Line String with Extra Space (Preserved Indentation)
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 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?
12 Answers
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.
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.
NOTE: It is significant that the last closing parenthesis is on another line. The END text must appear on a line by itself.
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
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
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" \ > filenameIt does not work if you put
"\n"just before\on the end of a line.Alternatively, you can use
printffor better portability (I happened to have a lot of problems withecho):printf '%s\n' \ "this is line one" \ "this is line two" \ "this is line three" \ > filenameYet 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" > filenameor
text='' text+="this is line one\n" text+="this is line two\n" text+="this is line three\n" printf "%b" "$text" > filenameAnother solution is achieved by mixing
printfandsed.if something then printf '%s' ' this is line one this is line two this is line three ' | sed '1d;$d;s/^ //g' fiIt 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"
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.
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.
Must Read
Hard to read:
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