There's nothing stopping you passing newline characters to echo
. Inside single or double quotes, in Bourne-like shells, newline is not any more special than any other characters¹
if [ ! -e "$file" ]; then
echo "server {
listen 80 default_server;
listen [::]:80 default_server;
server_name $server;
root /usr/share/nginx/html;
}" > "$file"
fi
Note that we're not adding the last line delimiter as echo
adds on itself.
If the text to output may contain backslashes or start with -
, you may want to use printf
instead. Either as printf '%s\n' "text"
so a trailing newline is added automatically like for echo
or printf %s
and add the newline in the text to be output:
if [ ! -e "$file" ]; then
printf %s "server {
listen 80 default_server;
listen [::]:80 default_server;
server_name $server;
root /usr/share/nginx/html;
}
" > "$file"
fi
With printf '%s\n' ...
you can also pass more than one argument to printf
will will be printed each with an added newline characters, so you can do:
new_lines=(
'server {'
' listen 80 default_server;'
' listen [::]:80 default_server;'
" server_name $server;"
' root /usr/share/nginx/html;'
'}'
)
[ -e "$file" ] || printf '%s\n' "${new_lines[@]}" > "$file"
Where you can decide the kind of quoting to use for each line.
Beware though that if passed no argument printf '%s\n'
will still print one empty line. In zsh
, you can use print -rC1 --
instead. Or you could define a:
println() {
[ "$#" -eq 0 ] || printf '%s\n' "$@"
}
helper function.
Also note the noclobber
option of POSIX shells that causes >
redirections operators to fail if that target exists as a regular file.
So:
set -o noblobber # same as set -C
printf '%s\n' "$newcontent" > "$file"
Would make sure $newcontent
is not written to the $file
if it already existed (as a regular file; if it were a device (like a symlink to /dev/null
) or a directory or fifo, etc, $newcontent
would still be written there; for that reason it doesn't fully avoid the TOCTOU race condition as the shell still needs to check the type of the file first).
To avoid writing to a file that already exists, regardless of its type, in a race-free way, you can use zsh
which can let you open files with the O_EXCL
flag:
zmodload zsh/system
(sysopen -w -o excl -u 1 -- $file && print -r -- $newcontent > $file)
¹ Except when preceded by a backslash inside double quotes.