Как я могу сделать так, чтобы оператор bash backtick содержал переводы строк в выходных данных?

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
slowpoison
источник

Ответы:

54

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

$ var=`echo line one && echo line two`
$ echo "$var"
line one
line two
Кир
источник
Итак, bash удаляет символы новой строки при расширении переменной в командной строке? И это поведение может быть отключено путем цитирования переменной?
медленная отрава
7
bashвыполняет параметры расширения перед выполнением команды, цитируя необходимо сообщить , bashчто вы хотите напечатать одну строку , а не 4 слов ( line, one, lineи two). Попробуйте: echo a <many spaces> bи echo "a <many spaces> b". Посмотрите на этот ответ тоже.
cYrus
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добавит завершающий перевод строки по умолчанию, чтобы все было в порядке.

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

Гастон
источник