Хоча @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