Aunque @cyrus es correcto, en realidad no responde a toda la pregunta, y no hay una explicación de lo que está sucediendo.
Así que vamos a atravesarlo.
Nuevas líneas en cadena
Primero determine la secuencia de bytes que se espera:
$ { echo a; echo b; } | xxd
0000000: 610a 620a a.b.
Ahora, use la sustitución de comandos (Sección 3.5.4) para intentar capturar esta secuencia de bytes:
$ x=$( echo a; echo b; )
Luego, haga un eco simple para verificar la secuencia de bytes:
$ echo $x | xxd
0000000: 6120 620a a b.
Parece que la primera línea nueva fue reemplazada por un espacio, y la segunda línea nueva quedó intacta. Pero por qué ?
Veamos lo que realmente está sucediendo aquí:
Primero, bash hará la expansión de parámetros de Shell (Sección 3.5.3)
El carácter '$' introduce la expansión de parámetros, la sustitución de comandos o la expansión aritmética. El nombre o símbolo del parámetro a expandir puede estar encerrado entre llaves, que son opcionales pero sirven para proteger la variable a expandir de los caracteres inmediatamente posteriores que podrían interpretarse como parte del nombre.
Luego bash hará la división de palabras (Sección 3.5.7)
El shell escanea los resultados de la expansión de parámetros, la sustitución de comandos y la expansión aritmética que no ocurrieron entre comillas dobles para la división de palabras.
El shell trata cada carácter de $ IFS como un delimitador, y divide los resultados de las otras expansiones en palabras sobre estos caracteres. Si IFS no está configurado, o su valor es exactamente, ...
A continuación, bash lo tratará como un comando simple (Sección 3.2.1)
Un comando simple es el tipo de comando que se encuentra con mayor frecuencia. Es solo una secuencia de palabras separadas por espacios en blanco, terminadas por uno de los operadores de control del shell (ver Definiciones). La primera palabra generalmente especifica un comando para ser ejecutado, y el resto de las palabras son los argumentos de ese comando.
La definición de espacios en blanco (Sección 2 - Definiciones)
en blanco Un espacio o un carácter de tabulación.
Finalmente, bash invoca el comando interno echo (Sección 4.2 - Bash Builtin Commands)
... Emite los argumentos, separados por espacios, terminados en una nueva línea. ...
Para resumir, las nuevas líneas se eliminan mediante Word Splitting, y luego echo obtiene 2 args, "a" y "b", y luego los separa por espacios y termina con una nueva línea.
Al hacer lo que @cyrus sugirió (y suprimir la nueva línea del eco con -n), el resultado es mejor:
$ echo -n "$x" | xxd
0000000: 610a 62 a.b
Líneas nuevas al final de la cadena
Sin embargo, todavía no es perfecto, la nueva línea final se ha ido. Mirando más de cerca la sustitución de comandos (Sección 3.5.4) :
Bash realiza la expansión ejecutando el comando y reemplazando la sustitución del comando con la salida estándar del comando, con cualquier nueva línea final eliminada.
Ahora que está claro por qué la nueva línea desaparece, se puede engañar a bash para que la conserve. Para hacer esto, agregue una cadena adicional al final y elimínela cuando use la variable:
$ x=$( echo a; echo b; echo -n extra )
$ echo -n "${x%extra}" | xxd
0000000: 610a 620a a.b.
TL; DR
Agregue una parte adicional al final de la salida y cite las variables:
$ 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