Як я можу змусити оператора bash backtick утримувати нові рядки на виході?


36

Чи є спосіб зробити так, щоб баш не їв нові рядки під час заміни бетіка?

Наприклад:

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

Відповіді:


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 зробить розширення параметра Shell (Розділ 3.5.3)

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

Тоді bash виконає Розщеплення слів (Розділ 3.5.7)

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

Оболонка розглядає кожен символ $ IFS як роздільник, і розбиває результати інших розширень на слова з цих символів. Якщо IFS не встановлено, або його значення точно, ...

Далі, bash трактуватиме це як просту команду (Розділ 3.2.1)

Проста команда - це така команда, яка зустрічається найчастіше. Це лише послідовність слів, розділених пробілами, що закінчується одним з операторів керування оболонкою (див. Визначення). Перше слово загалом визначає команду, яку потрібно виконати, решта слів - це аргументи цієї команди.

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

порожній символ пробілу чи вкладки.

Нарешті, bash викликає внутрішню команду echo (Розділ 4.2 - Bash Builtin Commands)

... Виведіть аргументи, розділені пробілами, закінчуючи новим рядком. ...

Отже, підсумовуючи, нові рядки видаляються Word Splitting, а потім луна отримує 2 аргументи, "a" та "b", а потім виводить їх відокремленими пробілами та закінчуючи новим рядком.

Виконуючи те, що запропонував @cyrus (і придушити новий рядок від лунки з -n), результат краще:

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

Нові рядки в кінці рядка

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

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, але я не можу поки прокоментувати відповіді. Тож я повторю частину цирусу його відповідь для повноти.

Що відбувається в першому рядку вашого коду, це те, що дійсно підстановка задніх посилань з'їдає останні рядки виводу заміщеної команди, як це зафіксовано . Але це не їсть новий рядок між вашими двома рядками.

У другому рядку коду відбувається те, що луна виконується двома аргументами, першим та другим рядком. Цитування розширеної змінної виправить таке: echo "$var"Це збереже новий рядок між вашим рядком першим та другим рядком. І echoдодамо заздалегідь новий рядок за замовчуванням, щоб все було в порядку.

Тепер, якщо ви хотіли зберегти всі зворотні нові рядки у висновку підстановки команд, вирішенням цього завдання є додавання виводу іншого рядка, який ви можете видалити після підстановки команди, дивіться тут .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.