8

I'm trying to store multiple lines in a bash variable, but it doesn't seem to work.

For example, if I list /bin one file per line and store it in $LS, then I pass $LS as stdin to wc, it always returns 1:

$ ls -1 /bin | wc -l
134
$ LS=$(ls -1 /bin); wc -l <<< $LS
1

If I try to output to screen, I get various results: echo prints all the lines on a single line, while printf prints only the first line:

#!/bin/bash
LS=$(ls -1 /bin)
echo $LS
printf $LS

So, a bash variable can contain multiple lines?

14

You need to double quote it (and you should double quote variables in most case):

echo "$LS"

But don't use echo to print variables content, using printf instead:

printf '%s\n' "$LS"
  • 1
    Just to put a finer point on the overall answer: printf expects a format string to be its first parameter, which is why they didn't see what they expected. If they want to see newlines in the output from printf, arbtrarily split on spaces, printf "%s\n" $LS would do it. – Jeff Schaller Jul 9 '15 at 12:39
  • the %LS should be $LS, btw (I couldn't make such a short edit) – Jeff Schaller Jul 9 '15 at 12:40
  • Thanks! It of course works also with wc and other commands: wc -l <<< "$LS" – Wizard79 Jul 9 '15 at 12:43
  • 1
    @JeffSchaller: No, you shouldn't unquote variable in this case, just using printf '%s\n' "$LS" if you want to see newline. That's mis-typing, fixed it! – cuonglm Jul 9 '15 at 13:03
  • Why shouldn't you use echo to print variables ? – 123 Jul 9 '15 at 14:31
8

The newlines are in the variable. LS=$(ls -1) sets the variable LS to the output of ls -1 (which produces the same output as ls, by the way, except when the output goes to a terminal), minus trailing newlines.

The problem is that you're removing the newlines when you print out the value. In a shell script, $LS does not mean “the value of the variable LS”, it means “take the value of LS, split it into words according to IFS and interpret each word as a glob pattern”. To get the value of LS, you need to write "$LS", or more generally to put $LS between double quotes.

echo "$LS" prints the value of LS, except in some shells that interpret backslash characters, and except for a few values that begin with -.

printf "$LS" prints the value of LS as long as it doesn't contain any percent or backslash character and (with most implementations) doesn't start with -.

To print the value of LS exactly, use printf %s "$LS". If you want a newline at the end, use printf '%s\n' "$LS".

Note that $(ls) is not, in general, the list of files in the current directory. This only works when you have sufficiently tame file names. To get the list of file names (except dot files), you need to use a wildcard: *. The result is a list of strings, not a string, so you can't assign it to a string variable; you can use an array variable files=(*) in shells that support them (ksh93, bash, zsh).

For more information, see Why does my shell script choke on whitespace or other special characters?

  • printf "$LS" would also succumb to backslash character parsing, similar to echo – cnst Nov 5 '15 at 14:29
0

The aforementioned

printf '%s\n' "$LS"

is the only correct solution.

Let me demonstrate that the other proposed solutions, both with echo and printf, simply do not work properly:

$ mkdir t
$ cd t
$ touch \\n
$ LS=$(ls -l)
$ echo "$LS"
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12

$ printf "$LS\n"
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12

$ printf '%s\n' "$LS"
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12 \n
$ ls -l
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12 \n
$ rm \\n
$ cd ..
$ rmdir t
$

(One can also test with V=$(printf 'line0:\\n\\n\nline1.\n') printf '%s\n' "$V" and variations.)

Whereas \n in the filenames may not be that common, store git diff into a variable, and your chances of encountering literal \n increase dramatically.

  • Note that ksh and zsh also support print -r -- "$LS" and zsh also has echo -E - $LS. csh has echo $LS:q though that doesn't output the newline character if $LS is empty, and getting a multiline value into $LS in the first place is quite tricky in csh. – Stéphane Chazelas Nov 5 '15 at 14:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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