36

Есть ли способ заставить bash не есть новые строки в замене обратного удара?

Например:

var=`echo line one && echo line two`
echo $var

line one line two

Что я хочу это

var=`echo line one && echo line two` # plus some magic
echo $var

line one
line two

3 ответа3

52

Это не проблема с заменой обратных кавычек, но с echo ; Вы должны заключить переменную в кавычки, чтобы заставить работать управляющие символы:

$ var=`echo line one && echo line two`
$ echo "$var"
line one
line two
5

Несмотря на то, что @cyrus верен - он на самом деле не отвечает на весь вопрос, и нет объяснения тому, что происходит.

Итак, давайте пройдемся по нему.

Новые строки в строке

Сначала определите ожидаемую последовательность байтов:

$ { echo a; echo b; } | xxd
0000000: 610a 620a                                a.b.

Теперь используйте команду подстановки (раздел 3.5.4), чтобы попытаться захватить эту последовательность байтов:

$ x=$( echo a; echo b; )

Затем выполните простое эхо для проверки последовательности байтов:

$ echo $x | xxd
0000000: 6120 620a                                a b.

Таким образом, кажется, что первая новая строка была заменена пробелом, а вторая новая строка осталась нетронутой. Но почему ?

Давайте посмотрим, что на самом деле здесь происходит:

Во-первых, bash сделает расширение параметров оболочки (раздел 3.5.3).

Символ '$' вводит расширение параметра, подстановку команд или арифметическое расширение. Имя параметра или символ, который должен быть расширен, может быть заключен в фигурные скобки, которые являются необязательными, но служат для защиты переменной, которая должна быть развернута, от символов, следующих непосредственно за ним, которые могут быть интерпретированы как часть имени.

Затем bash выполнит разделение слов (раздел 3.5.7).

Оболочка сканирует результаты раскрытия параметров, подстановки команд и арифметического раскрытия, которые не встречались в двойных кавычках для разделения слов.

Оболочка обрабатывает каждый символ $ IFS как разделитель и разбивает результаты других расширений на слова на этих символах. Если IFS не установлен, или его значение точно, ...

Далее, bash будет обращаться с ней как с простой командой (раздел 3.2.1).

Простая команда - это команда, с которой чаще всего сталкиваются. Это просто последовательность слов, разделенных пробелами, оканчивающихся одним из управляющих операторов оболочки (см. Определения). Первое слово обычно определяет команду, которая должна быть выполнена, а остальные слова являются аргументами этой команды.

Определение пробелов (Раздел 2 - Определения)

пробел или символ табуляции.

Наконец, bash вызывает внутреннюю команду echo (Раздел 4.2 - Команды Bash)

... Выведите аргументы, разделенные пробелами, оканчивающиеся новой строкой. ...

Таким образом, чтобы подвести итог, новые строки удаляются Word Splitting, а затем echo получает 2 аргумента, "a" и "b", а затем выводит их разделенные пробелами и заканчивая новой строкой.

Делая то, что предложил @cyrus (и подавив перевод строки из echo с -n), результат получится лучше:

$ echo -n "$x" | xxd
0000000: 610a 62                                  a.b

Новые строки в конце строки

Это все еще не идеально, хотя завершающий символ новой строки исчез. Посмотрим ближе на подстановку команд (раздел 3.5.4):

Bash выполняет расширение, выполнив команду и заменив подстановку команды стандартным выводом команды, при этом все завершающие символы новой строки будут удалены.

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

$ x=$( echo a; echo b; echo -n extra )
$ echo -n "${x%extra}" | xxd
0000000: 610a 620a                                a.b.

TL; DR

Добавьте дополнительную часть в конец вывода и заключите в кавычки переменные:

$ cat /no/file/here 2>&1 | xxd
0000000: 6361 743a 202f 6e6f 2f66 696c 652f 6865  cat: /no/file/he
0000010: 7265 3a20 4e6f 2073 7563 6820 6669 6c65  re: No such file
0000020: 206f 7220 6469 7265 6374 6f72 790a        or directory.
$ cat /no/file/here 2>&1 | cksum
3561909523 46
$ 
$ var=$( cat /no/file/here 2>&1; rc=${?}; echo extra; exit ${rc})
$ echo $?
1
$ 
$ echo -n "${var%extra}" | xxd
0000000: 6361 743a 202f 6e6f 2f66 696c 652f 6865  cat: /no/file/he
0000010: 7265 3a20 4e6f 2073 7563 6820 6669 6c65  re: No such file
0000020: 206f 7220 6469 7265 6374 6f72 790a        or directory.
$ echo -n "${var%extra}" | cksum
3561909523 46
0

Я бы добавил свое небольшое дополнение в комментарий к ответу cYrus, но пока не могу комментировать ответы, которые мне кажется. Поэтому я напишу часть ответа cYrus для полноты.

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

Во второй строке кода происходит то, что echo выполняется с двумя аргументами: первой и второй. Кавычка в расширенной переменной исправит это: echo "$var" Это сохранит новую строку между первой и второй строкой. И echo добавит завершающий перевод строки по умолчанию, чтобы все было в порядке.

Теперь, если вы хотите сохранить все завершающие символы новой строки в выходных данных некоторой подстановки команд, обходной путь должен добавить еще одну строку выходных данных, которую можно удалить после подстановки команд, см. Здесь .

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .