Echoing/printing a bash `ls` command substitution without quotes
I have the following example:
$ a="$(ls)"
$ echo $a
backups cache crash lib local lock log mail opt run snap spool tmp
$
$ echo "$a"
backups
cache
crash
lib
local
lock
log
mail
opt
run
snap
spool
tmp
Now with printf
:
$ printf $a
backups
$
$ printf "$a"
backups
cache
crash
lib
local
lock
log
mail
opt
run
snap
spool
tmp
Why is the output so different? What do quotes do in this situation?
Could someone explain what’s going on here?
P.S. Found some explanation on the ls
behavior:
Output from ls has newlines but displays on a single line. Why?
https://superuser.com/questions/424246/what-is-the-magic-separator-between-filenames-in-ls-output
http://mywiki.wooledge.org/ParsingLs
The newline characters can be checked this way:
ls | od -c
echo $a
is the same as
echo backups cache crash lib local lock log mail opt run snap spool tmp
whereas echo "$a"
is the same as
echo 'backups
cache
crash
lib
local
lock
log
mail
opt
run
snap
spool
tmp'
See https://mywiki.wooledge.org/Quotes.
The first argument for printf
is a formatting string and printf $a
is the same as printf backups cache crash lib local lock log mail opt run snap spool tmp
so it’s using the string backups
as the format and discarding the rest since there’s nothing like %s
in the formatting string to use them in. Just like:
$ printf foo whatever
foo$
$ printf '%sn' foo whatever
foo
whatever
Don’t do a="$(ls)"
to try to create a scalar variable holding file names btw as that’s fragile, do a=(*)
to hold them in an array instead.
Thanks to @Ed Morton and @Kusalananda for the explanation.
I guess my problem was that I always thought that, by default, ls
splits the files using spaces or tabs. But in fact, it turned out that it separates them with new line characters but outputs the files in columns (sorted vertically) when printing to a terminal. Newline characters can be checked with:
ls | od -c
I’ll move the @Kusalananda’s answer from the comment section to an answer, since it was helpful:
In short, echo
outputs its arguments with spaces between them. This is what echo
does.
When using echo $a
, the shell splits the contents of $a
into several arguments based on spaces, tabs and newlines (and then also does filename globbing on each generated word). echo
then prints them with spaces between them.
With echo "$a"
, you only ever give echo a single argument. The string "$a"
contains newlines from the output of ls
, and these are retained and outputted by echo
.