Meskipun @cyrus benar - sebenarnya tidak menjawab seluruh pertanyaan, dan tidak ada penjelasan tentang apa yang terjadi.
Jadi mari kita berjalan melewatinya.
Baris baru dalam string
Pertama tentukan urutan byte yang diharapkan:
$ { echo a; echo b; } | xxd
0000000: 610a 620a a.b.
Sekarang, gunakan Substitusi Perintah (Bagian 3.5.4) untuk mencoba menangkap urutan byte ini:
$ x=$( echo a; echo b; )
Kemudian, lakukan gema sederhana untuk memverifikasi urutan byte:
$ echo $x | xxd
0000000: 6120 620a a b.
Jadi sepertinya baris baru pertama diganti dengan spasi, dan baris baru kedua dibiarkan utuh. Tapi kenapa ?
Mari kita lihat apa yang sebenarnya terjadi di sini:
Pertama, bash akan melakukan Shell Parameter Expansion (Bagian 3.5.3)
Karakter '$' memperkenalkan ekspansi parameter, substitusi perintah, atau ekspansi aritmatika. Nama parameter atau simbol yang akan diperluas dapat dilampirkan dalam kurung kurawal, yang merupakan opsional tetapi berfungsi untuk melindungi variabel agar diperluas dari karakter segera setelah itu yang dapat diartikan sebagai bagian dari nama.
Kemudian bash akan melakukan Word Splitting (Bagian 3.5.7)
Shell memindai hasil ekspansi parameter, penggantian perintah, dan ekspansi aritmatika yang tidak terjadi dalam tanda kutip ganda untuk pemisahan kata.
Shell memperlakukan setiap karakter $ IFS sebagai pembatas, dan membagi hasil ekspansi lainnya menjadi kata-kata pada karakter ini. Jika IFS tidak disetel, atau nilainya tepat, ...
Selanjutnya, bash akan memperlakukannya sebagai Perintah Sederhana (Bagian 3.2.1)
Perintah sederhana adalah jenis perintah yang paling sering ditemui. Itu hanya urutan kata-kata yang dipisahkan oleh kosong, diakhiri oleh salah satu operator kontrol shell (lihat Definisi). Kata pertama umumnya menentukan perintah yang akan dieksekusi, dengan sisa kata-kata yang menjadi argumen perintah itu.
Definisi kosong (Bagian 2 - Definisi)
blank Karakter spasi atau tab.
Akhirnya, bash memanggil perintah internal echo (Bagian 4.2 - Bash Builtin)
... Keluarkan argumen, dipisahkan oleh spasi, diakhiri dengan baris baru. ...
Jadi untuk meringkas, baris baru dihapus oleh Word Splitting, dan kemudian gema mendapat 2 argumen, "a", dan "b", dan kemudian output mereka dipisahkan oleh spasi dan diakhiri dengan baris baru.
Melakukan apa yang disarankan @cyrus (dan menekan baris baru dari echo dengan -n) hasilnya lebih baik:
$ echo -n "$x" | xxd
0000000: 610a 62 a.b
Baris baru di akhir string
Ini masih belum sempurna, baris baru yang tertinggal hilang. Melihat lebih dekat pada Substitusi Perintah (Bagian 3.5.4) :
Bash melakukan ekspansi dengan mengeksekusi perintah dan mengganti substitusi perintah dengan output standar dari perintah, dengan setiap baris baru yang tertinggal dihapus.
Sekarang sudah jelas mengapa baris baru hilang, bash bisa dibodohi untuk menjaganya. Untuk melakukan ini, tambahkan string tambahan ke bagian akhir, dan hapus ketika menggunakan variabel:
$ x=$( echo a; echo b; echo -n extra )
$ echo -n "${x%extra}" | xxd
0000000: 610a 620a a.b.
TL; DR
Tambahkan bagian ekstra ke akhir output, dan kutip variabel:
$ 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