How to write multiple line string using Bash with variables?
Asked Answered
H

9

332

How can I write multi-lines in a file called myconfig.conf using BASH?

#!/bin/bash
kernel="2.6.39";
distro="xyz";

echo <<< EOL
line 1, ${kernel}
line 2,
line 3, ${distro}
line 4
line ...
EOL >> /etc/myconfig.conf;
cat /etc/myconfig.conf;
Harlin answered 24/10, 2011 at 12:23 Comment(0)
T
644

The syntax (<<<) and the command used (echo) is wrong.

Correct would be:

#!/bin/bash

kernel="2.6.39"
distro="xyz"
cat >/etc/myconfig.conf <<EOL
line 1, ${kernel}
line 2, 
line 3, ${distro}
line 4 line
... 
EOL

cat /etc/myconfig.conf

This construction is referred to as a Here Document and can be found in the Bash man pages under man --pager='less -p "\s*Here Documents"' bash.

Teniafuge answered 24/10, 2011 at 12:29 Comment(13)
and if you want to append it would be cat >>Downtrodden
This works great, but you have to make sure that there's no whitespace in front of the terminating EOF, otherwise it will not be recognized, and you will run into an unexpected end of file error.Eastbound
Is there a term for this?Wolverine
@StevenEckhoff This is called a heredoc.Symptom
What if I need sudo permission to write to the file?Rove
@Rove You can use tee for that, like cat << EOL | sudo tee /etc/myconfig.confGeoras
@Rove If using tee, don't forget that you can need -a to appendBinding
what if you want to literally write "${distro}" to a file instead of substituting it for "xyz"?Fabrizio
@Fabrizio As usual, you can escape characters with \. In your case, it would be \${distro}.Kohler
Now, how do you do this withOUT expanding variables inside the multi-line string?Dispensary
Got it: simply put the first EOL usage in quotes. See here: linuxize.com/post/bash-heredoc. That makes the first line of the heredoc look like this: cat > /etc/myconfig.conf << 'EOL' instead of like this: cat > /etc/myconfig.conf << EOL. Now it will NOT do variable expansion before writing to the file. Excellent! Note: I know that's kind of the opposite of this question, but that's what I want, and googling for it landed me here.Dispensary
This doesn't work for me :(Scribbler
The last line ends with the delimiting identifier. White space in front of the delimiter is not allowed, as documented in linuxize.com/post/bash-heredoc So, if you use this in a function, it doesn't work if you add spaces or tab in front of EOLSlouch
U
106
#!/bin/bash
kernel="2.6.39";
distro="xyz";

cat > /etc/myconfig.conf << EOL
line 1, ${kernel}
line 2,
line 3, ${distro}
line 4
line ...
EOL

this does what you want.

Uglify answered 24/10, 2011 at 12:28 Comment(3)
@Teniafuge I was typing not faster, but less letters than you. ^_*Uglify
You were faster but its not enough :))))Bailee
This helped a lot!Pantaloons
G
55

If you do not want variables to be replaced, you need to surround EOL with single quotes.

cat >/tmp/myconfig.conf <<'EOL'
line 1, ${kernel}
line 2, 
line 3, ${distro}
line 4 line
... 
EOL

Previous example:

$ cat /tmp/myconfig.conf 
line 1, ${kernel}
line 2, 
line 3, ${distro}
line 4 line
... 
Georgena answered 27/10, 2015 at 4:26 Comment(1)
Thanks for nuance when I don't wanna modify variables. e.g. having it in quotes 'EOL'Muniment
S
20

The heredoc solutions are certainly the most common way to do this. Other common solutions are:

echo 'line 1, '"${kernel}"'
line 2,
line 3, '"${distro}"'
line 4' > /etc/myconfig.conf

and

exec 3>&1 # Save current stdout
exec > /etc/myconfig.conf
echo line 1, ${kernel}
echo line 2, 
echo line 3, ${distro}
...
exec 1>&3  # Restore stdout

and

printf "%s\n" "line1, ${kernel}" "line2," "line3, $distro" ...
Symptom answered 24/10, 2011 at 14:56 Comment(1)
Maybe also point to printf which introduces yet some more interesting variations.Mercurochrome
F
11

I'm using Mac OS and to write multiple lines in a SH Script following code worked for me

#! /bin/bash
FILE_NAME="SomeRandomFile"

touch $FILE_NAME

echo """I wrote all
the  
stuff
here.
And to access a variable we can use
$FILE_NAME  

""" >> $FILE_NAME

cat $FILE_NAME

Please don't forget to assign chmod as required to the script file. I have used

chmod u+x myScriptFile.sh
Falciform answered 7/8, 2020 at 15:29 Comment(0)
D
4

Below mechanism helps in redirecting multiple lines to file. Keep complete string under " so that we can redirect values of the variable.

#!/bin/bash
kernel="2.6.39"
echo "line 1, ${kernel}
line 2," > a.txt
echo 'line 2, ${kernel}
line 2,' > b.txt

Content of a.txt is

line 1, 2.6.39
line 2,

Content of b.txt is

line 2, ${kernel}
line 2,
Delay answered 24/1, 2018 at 10:10 Comment(0)
S
0

In my usecase I had to use the following:

{
 ...
 printf "Suites: $SUITES"
 printf "Components: $COMPONENTS"
 printf "Signed-By: $SIGNATURE"
 [ ! -z "$ARCH_STRING" ] && printf "$ARCH_STRING"
 [ ! -z "$LANG_STRING" ] && printf "$LANG_STRING"
 [ ! -z "$TARGET_STRING" ] && printf "$TARGET_STRING"
 ...
} > /tmp/myconfig.conf 

This because I did not want to write empty newlines to the file if the variables were not defined. The nice thing about this method is that you can write all kinds of arbitrary logic within the block if necessary.

And ofcourse, if you would like to append, you can also use >> here instead of > like you would in the other answers.

Stretch answered 9/7, 2023 at 7:27 Comment(0)
W
-1

I usually put template in file and use this templating engine:

### <template-file> [ARG=VALUE..]
## Variables are replaced only within "{{" and "}}" notation.
## Example:
##         $0 path-to-tmpl REF=master pass=xx
##         # The template may look like so:
##         #    $pass = ["user", "{{ $pass }}"];
##         # Resulting in:
##         #    $pass = ["user", "xxx"];
##~
template() {
    tmpl=$1
    shift

    for i in $@; do
        declare $i;
    done

    eval "echo \"$(sed -e 's/"/\\"/g' -e 's/\$/\\$/g' -e 's/{{\s*\\\(\$\w*\)\s*}}/\1/g' $tmpl)\""
}
Weslee answered 17/10, 2021 at 15:48 Comment(0)
W
-3

another simpler way I think but definitely for small number of lines

touch myfile.txt
echo "line1">>myfile.txt
echo "line2">>myfile.txt
echo "line3">>myfile.txt
echo "line4">>myfile.txt
Wraith answered 1/6, 2021 at 13:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.