Почему переменное присваивание заменяет вкладки пробелами

Почему переменная назначения заменяет вкладки пробелами в оболочке?

$ cat tmp
a    b e    c    d
$ res=$(cat tmp)
$ echo $res
a b e c d

Ответ 1

Вам нужно процитировать переменную $res для пробела, которая должна быть сохранена.

$ cat file
a       b e     c       d

$ res=$(cat file)

$ echo $res
a b e c d

$ echo "$res"
a       b e     c       d

От man bash под QUOTING:

Quoting используется для удаления специального значения определенных символов или слов в оболочке. Цитирование может использоваться для отключения специальной обработки для специальных символов, для предотвращения сохранения зарезервированных слов как таковых и предотвращения расширения параметров.

Каждый из метасимволов, перечисленных выше в разделе ОПРЕДЕЛЕНИЯ, имеет особое значение для оболочки и должен быть указан, если он должен представлять себя.

...

\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

...

Ответ 2

Это не назначение, которое теряет вкладки, но вызывает команду echo.

res присваивается значение, которое включает вкладки. Когда в оболочке вы записываете $res что эквивалентно набору содержимого переменной res в этой точке.

Так:

$ echo $res

делает то же самое, что:

$ echo a    b e     c       d

(где большие пробелы в этой строке являются символами табуляции, которые можно ввести, нажав Ctrl+V Tab). И если вы запустите эту команду, вы также получите:

a b e c d

Итак, на самом деле ваш вопрос: почему в аргументах команды отсутствуют закладки?

Ответ заключается в том, что команда (echo в этом случае) никогда не видит вкладки или даже пробелы. Оболочка анализирует вашу командную строку в имени команды и списке аргументов. Он использует белое пространство (вкладки и пробелы) для разделения команды на эти части. Затем он запускает команду, передавая ей список аргументов.

Итак, какое echo получает в качестве аргументов список "a", "b", "e", "c", d; он не знает, какие персонажи изначально их разделяли.

То, что echo then делает, выводит каждый из его аргументов, пробел между ними. Отсюда вывод, который вы видите. Если исходная командная строка использовала одиночный пробел для разделения каждого аргумента, вывод соответствует входу, поэтому он выглядит скорее так же, как и пробелы на входе, но они не являются: оболочка собирает исходные пространства и echo вставляет некоторые новые.

Маркеры комментариев могут использоваться, чтобы заставить оболочку рассматривать несколько слов как один аргумент. Например, если вы выполните:

$ echo a    "b    c"    d

который передает 3 аргумента для echo: 'a,' b c и 'd. Средний аргумент содержит 4 пробела; те передаются в echo, поэтому будут отображаться на его выходе. Пространства вне кавычек используются оболочкой для разделения аргументов, поэтому они не передаются в echo. Следовательно, выход:

a b    c d

Чтобы проверить эту вещь, яснее использовать команду, которая показывает, сколько именно аргументов получено и что было в каждом из них. Этот однострочный движок Perl сделает это:

$ 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'
        ];