0

I'm learning about bash shell and environment variables.

I have tested echo a env. var surrounded by quotes and without them (I put it like it appears on my terminal):

user@pc:~$ echo "$BASE_DEPENDENCIES"
build-essential \
                   cmake           \
                   debhelper       \
                   mesa-utils      \
                   cppcheck        \
                   xsltproc        \
                   python-psutil   \
                   python          \
                   bc              \
                   netcat-openbsd  \
                   gnupg2          \
                   net-tools       \
                   locales
user@pc:~$ echo $BASE_DEPENDENCIES
build-essential \ cmake \ debhelper \ mesa-utils \ cppcheck \ xsltproc \ python-psutil \ python \ bc \ netcat-openbsd \ gnupg2 \ net-tools \ locales

The first one has new lines and spaces. The second one is only one line.

Why does it happen?

By the way, my system is an Ubuntu 17.04.

3 Answers3

2

There's a sequence of expansions that bash does on inputs:

The order of expansions is: brace expansion; tilde expansion, parameter and variable expansion, arithmetic expansion, and command substitution (done in a left-to-right fashion); word splitting; and filename expansion.

The unquoted version allows for word splitting (with my emphasis):

The shell scans the results of parameter expansion, command substitution, and arithmetic expansion that did not occur within double quotes for word splitting.

... and with a default value of $IFS, this means that build-essential \(newline)(other spaces)cmake ... turns into multiple words:

  • build-essential
  • \
  • cmake
  • ...

... effectively removing the newline character as well as all-but-one of each of the space characters.

Quoting the variable prevents word splitting (among other things).

Further reading:

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
1

The shell separates parameters to commands with 1 or more whitespace (i.e., space, tab, newline) characters. When you do not include quotes, the value of $BASE_DEPENDENCIES gets expanded to N parameters. echo takes all its parameters and prints each of them separated by single spaces, so you get:

build-essential<space>\<space>cmake<space>\...

Quotes override the space-delimiting; everything within the quotes is passed as a single argument. In that case, the value of $BASE_DEPENDENCIES gets passes a 1 parameter to echo. echo then prints that one parameter as it was supplied (spaces, newlines, etc. intact).

To better understand, consider this simple shell function, which is equivalent to a command in this case:

foo() {
    for i in "${@}"; do
        echo "->'${i}'"
    done
}

Say I have a variable that looks like:

x="    1         2 3    4   "

Without quotes I get 4 arguments:

$ foo $x
->'1'
->'2'
->'3'
->'4'

With quotes I get only one:

$ foo "$x"
->'    1         2 3    4   '
Andy Dalton
  • 13,993
0

That's because quoting preserves whitespace. Compare with this:

# a=$(cal)
# echo $a
October 2017 Su Mo Tu We Th Fr Sa 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
# echo "$a"
    October 2017      
Su Mo Tu We Th Fr Sa  
 1  2  3  4  5  6  7  
 8  9 10 11 12 13 14  
15 16 17 18 19 20 21  
22 23 24 25 26 27 28  
29 30 31  

First the cal command output is stored in a. Then it's printed without and with quoting. As you can see, quoting preserves whitespace, while no quoting breaks the text into tokens, which are separated by space.