11

I'm going through a script in Bash where, depending on the conditionals, I want to append to a variable different things and then display it at the very end. Something like this:

VAR="The "

if [[ whatever ]]; then
    VAR="$VAR cat wears a mask"
elif [[ whatevs ]]; then
    VAR="$VAR rat has a flask"
fi

But I run into difficulties if I try to use this form of building up VAR by appending to it when I want to occasionally append newlines into it. How would I do VAR="$VAR\nin a box", for example?

I have seen usage of $'\n' before, but not when also trying to use $VAR because of the appending.

3 Answers 3

15

Using ANSI C quoting:

var="$var"$'\n'"in a box"

You could put the $'\n' in a variable:

newline=$'\n'
var="$var${newline}in a box"

By the way, in this case, it's better to use the concatenation operator:

var+="${newline}in a box"

If you don't like ANSI C quoting, you can use printf with its -v option:

printf -v var '%s\n%s' "$var" "in a box"

Then, to print the content of the variable var, don't forget quotes!

echo "$var"

or, better yet,

printf '%s\n' "$var"

Remark. Don't use upper case variable names in Bash. It's terrible, and one day it will clash with an already existing variable!


You could also make a function to append a newline and a string to a variable using indirect expansion (have a look in the Shell Parameter Expansion section of the manual) as so:

append_with_newline() { printf -v "$1" '%s\n%s' "${!1}" "$2"; }

Then:

$ var="The "
$ var+="cat wears a mask"
$ append_with_newline var "in a box"
$ printf '%s\n' "$var"
The cat wears a mask
in a box
$ # There's no cheating. Look at the content of 'var':
$ declare -p var
declare -- var="The cat wears a mask
in a box"

Just for fun, here's a generalized version of the append_with_newline function that takes n+1 arguments (n≥1) and that will concatenate them all (with exception of the first one being the name of a variable that will be expanded) using a newline as separator, and puts the answer in the variable, the name of which is given in the first argument:

concatenate_with_newlines() { local IFS=$'\n'; printf -v "$1" '%s\n%s' "${!1}" "${*:2}"; }

Look how well it works:

$ var="hello"
$ concatenate_with_newlines var "a gorilla" "a banana" "and foobar"
$ printf '%s\n' "$var"
hello
a gorilla
a banana
and foobar
$ # :)

It's a funny trickery with IFS and "$*".

3

By default, Bash does not process escape characters. The assignment

VAR="foo\nbar"

assigns 8 characters to the variable VAR: 'f', 'o', 'o', '', 'n', 'b', 'a', and 'r'. The POSIX standard states that the echo command should treat the two-character string \n in its arguments as a linefeed; Bash does not follow the standard in this case and requires the -e option to enable this processing. The printf command follows the POSIX specification and treats literal "\n" in its argument as a linefeed, but does not expand such uses in strings that replace placeholders: printf "%s\n" "$VAR" would still output

foo\nbar

instead of

foo
bar

To include an actual linefeed character in a string, you can use ANSI quoting:

VAR=$'foo\nbar'

which has the drawback that the string is otherwise processed as a single-quoted string, and cannot contain parameter expansions or command substitutions. Another option is that a string that spans multiple lines will contain the quoted linefeed characters:

$ VAR="foo
> bar"
$ echo "$foo"
foo
bar
2

It works this way:

VAR=foo
VAR="$VAR\nrat has a flask"
echo -e "$VAR"

# or of you're using bash then use
printf "%b\n" "$VAR"

Output:

foo
rat has a flask
1
  • 1
    -e was the missing piece. this is the only post that shows it.
    – smoore4
    Commented Jun 17, 2022 at 20:14

Not the answer you're looking for? Browse other questions tagged or ask your own question.