Why does a variable assignment replace tabs with spaces in the shell?
$ cat tmp
a b e c d
$ res=$(cat tmp)
$ echo $res
a b e c d
Why does a variable assignment replace tabs with spaces in the shell?
$ cat tmp
a b e c d
$ res=$(cat tmp)
$ echo $res
a b e c d
You need to quote your variable
$res
for whitespace to be preserved.From
man bash
underQUOTING
:It isn't the assignment that's losing the tabs, but invoking the
echo
command.res
is assigned a value that includes tabs. When in the shell you write$res
that's the equivalent of typing in the contents of theres
variable at that point.So:
does the same as:
(where the big spaces in that line are tab characters, which can be typed by pressing
Ctrl+V Tab
). And if you run that command you also get:So your question actually is: why do tabs go missing in a command's arguments?
The answer is that the command (
echo
in this case) never sees the tabs, or indeed the spaces. The shell parses your command-line into a command name and a list of arguments. It uses white-space (tabs and spaces) to split the command into these parts. Then it runs the command, passing it the argument list.So what
echo
receives as its arguments is the list ‘a’, ‘b’, ‘e’, ‘c’, ‘d’; it has no idea what characters originally separated them.What
echo
then does is output each of its arguments, with a space between them. Hence the output you see. Where the original command line used a single space character to separate each argument the output matches the input, so it looks rather like the spaces in the input are also in the output — but they aren't: the shell gobbles up the original spaces andecho
inserts some new ones.Quote marks can be used to make the shell treat multiple ‘words’ as a single argument. For example if you do:
that is passing 3 arguments to
echo
: ‘a’, ‘b c’, and ‘d’. The middle argument contains 4 spaces; those get passed toecho
, so will appear in its output. The spaces outside the quote marks are used by the shell for splitting arguments, so aren't passed toecho
. Hence the output is:To check this kind of thing it's clearer to use a command which shows you exactly how many arguments it received and what was in each of them. This Perl one-liner will do that: