Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why a variable assignment replaces tabs with spaces

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
like image 754
MoreFreeze Avatar asked Jan 09 '13 08:01

MoreFreeze


People also ask

Can environment variable names have spaces?

Variable names aren't case-sensitive, and can include spaces and special characters.

How many number of spaces should be set in default tab?

The default value for the tab-size property is 8 space characters, and it can accept any positive integer value.


2 Answers

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 the res variable at that point.

So:

$ echo $res

does the same as:

$ echo a    b e     c       d

(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:

a b e c d

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 and echo 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:

$ echo a    "b    c"    d

that is passing 3 arguments to echo: ‘a’, ‘b    c’, and ‘d’. The middle argument contains 4 spaces; those get passed to echo, so will appear in its output. The spaces outside the quote marks are used by the shell for splitting arguments, so aren't passed to echo. Hence the output is:

a b    c d

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:

$ perl -MData::Dumper -E 'say Dumper \@ARGV' a    b    c    d
$VAR1 = [
          'a',
          'b',
          'c',
          'd'
        ];

$ perl -MData::Dumper -E 'say Dumper \@ARGV' "a    b    c    d"
$VAR1 = [
          'a    b    c    d'
        ];

$ perl -MData::Dumper -E 'say Dumper \@ARGV' a    "b    c"    d
$VAR1 = [
          'a',
          'b    c',
          'd'
        ];

$ res="a    b c     d"
$ perl -MData::Dumper -E 'say Dumper \@ARGV' $res
$VAR1 = [
          'a',
          'b',
          'c',
          'd'
        ];

$ perl -MData::Dumper -E 'say Dumper \@ARGV' "$res"
$VAR1 = [
          'a    b c     d'
        ];
like image 66
Smylers Avatar answered Oct 08 '22 16:10

Smylers


You need to quote your variable $res for whitespace to be preserved.

$ cat file
a       b e     c       d

$ res=$(cat file)

$ echo $res
a b e c d

$ echo "$res"
a       b e     c       d

From man bash under QUOTING:

Quoting is used to remove the special meaning of certain characters or words to the shell. Quoting can be used to disable special treatment for special characters, to prevent reserved words from being recognized as such, and to prevent parameter expansion.

Each of the metacharacters listed above under DEFINITIONS has special meaning to the shell and must be quoted if it is to represent itself.

...

\a     alert (bell)
\b     backspace
\e
\E     an escape character
\f     form feed
\n     new line
\r     carriage return
\t     horizontal tab
\v     vertical tab
\\     backslash
\'     single quote
\"     double quote
\nnn   the eight-bit character whose value is the octal value nnn
\xHH   the eight-bit character whose value is the hexadecimal value HH
\cx    a control-x character

...
like image 32
Chris Seymour Avatar answered Oct 08 '22 16:10

Chris Seymour