Mimo że @cyrus jest poprawny - tak naprawdę nie odpowiada na całe pytanie i nie ma wyjaśnienia, co się dzieje.
Przejdźmy więc to.
Nowe linie w ciągu
Najpierw określ oczekiwaną sekwencję bajtów:
$ { echo a; echo b; } | xxd
0000000: 610a 620a a.b.
Teraz użyj polecenia Podstawienie komendy (sekcja 3.5.4), aby spróbować przechwycić tę sekwencję bajtów:
$ x=$( echo a; echo b; )
Następnie wykonaj proste echo, aby sprawdzić sekwencję bajtów:
$ echo $x | xxd
0000000: 6120 620a a b.
Wygląda na to, że pierwsza nowa linia została zastąpiona spacją, a druga nowa linia pozostała nienaruszona. Ale dlaczego ?
Przyjrzyjmy się, co się tu właściwie dzieje:
Po pierwsze, bash wykona rozszerzenie parametru powłoki (rozdział 3.5.3)
Znak „$” wprowadza interpretację parametrów, podstawianie poleceń lub interpretację arytmetyczną. Nazwa parametru lub symbol, który ma zostać rozwinięty, może być ujęty w nawiasy klamrowe, które są opcjonalne, ale służą do ochrony zmiennej, która ma zostać rozwinięta przed znakami bezpośrednio po niej, które mogą być interpretowane jako część nazwy.
Następnie bash wykona dzielenie wyrazów (rozdział 3.5.7)
Powłoka skanuje wyniki interpretacji parametrów, podstawiania poleceń i interpretacji arytmetycznej, które nie wystąpiły w ramach podwójnych cudzysłowów dla podziału słów.
Powłoka traktuje każdy znak $ IFS jako ogranicznik i dzieli wyniki pozostałych rozszerzeń na słowa na tych znakach. Jeśli IFS jest rozbrojony lub jego wartość jest dokładnie, ...
Następnie bash potraktuje to jako proste polecenie (rozdział 3.2.1)
Proste polecenie to najczęściej spotykane polecenie. To tylko sekwencja słów oddzielona spacjami, zakończona jednym z operatorów sterujących powłoki (patrz Definicje). Pierwsze słowo zazwyczaj określa polecenie do wykonania, a pozostałe słowa to argumenty tego polecenia.
Definicja pustych miejsc (sekcja 2 - Definicje)
pusty Znak spacji lub tabulacji.
Wreszcie bash powołuje się na echo (sekcja 4.2 - Bash Builtin Commands) polecenie wewnętrzne
... Wyprowadzaj argumenty, oddzielone spacjami, zakończone znakiem nowej linii. ...
Podsumowując, nowe wiersze są usuwane przez podział tekstu, a następnie echo dostaje 2 argumenty, „a” i „b”, a następnie wyprowadza je oddzielone spacjami i kończąc na nowej linii.
Robiąc to, co sugerował @cyrus (i pomijaj echo nowej linii za pomocą -n), wynik jest lepszy:
$ echo -n "$x" | xxd
0000000: 610a 62 a.b
Nowe linie na końcu łańcucha
Wciąż jednak nie jest idealny, nowa linia zniknęła. Przyglądając się bliżej podstawieniu poleceń (sekcja 3.5.4) :
Bash wykonuje rozwinięcie, wykonując polecenie i zastępując podstawienie polecenia standardowym wyjściem polecenia, a wszelkie końcowe znaki nowej linii są usuwane.
Teraz, gdy jest jasne, dlaczego nowa linia odchodzi, bash można oszukać. Aby to zrobić, dodaj dodatkowy ciąg na końcu i usuń go, gdy używasz zmiennej:
$ x=$( echo a; echo b; echo -n extra )
$ echo -n "${x%extra}" | xxd
0000000: 610a 620a a.b.
TL; DR
Dodaj dodatkową część na końcu danych wyjściowych i cytuj zmienne:
$ 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