Mesmo que o @cyrus esteja correto - ele não responde a toda a pergunta e não há explicação do que está acontecendo.
Então, vamos percorrer isso.
Novas linhas na string
Primeiro, determine a sequência de bytes esperada:
$ { echo a; echo b; } | xxd
0000000: 610a 620a a.b.
Agora, use Substituição de Comando (Seção 3.5.4) para tentar capturar esta sequência de bytes:
$ x=$( echo a; echo b; )
Em seguida, faça um eco simples para verificar a sequência de bytes:
$ echo $x | xxd
0000000: 6120 620a a b.
Parece que a primeira nova linha foi substituída por um espaço e a segunda nova linha foi deixada intacta. Mas por que ?
Vamos analisar o que realmente está acontecendo aqui:
Primeiro, o bash fará a expansão dos parâmetros do shell (Seção 3.5.3)
O caractere '$' introduz expansão de parâmetro, substituição de comando ou expansão aritmética. O nome ou símbolo do parâmetro a ser expandido pode ser colocado entre chaves, que são opcionais, mas servem para proteger a variável a ser expandida dos caracteres imediatamente a seguir, que podem ser interpretados como parte do nome.
Então o bash fará a Divisão de Palavras (Seção 3.5.7)
O shell varre os resultados de expansão de parâmetro, substituição de comando e expansão aritmética que não ocorreram entre aspas duplas para divisão de palavras.
O shell trata cada caractere do $ IFS como um delimitador e divide os resultados das outras expansões em palavras nesses caracteres. Se o IFS não estiver definido ou seu valor for exatamente, ...
Em seguida, o bash o tratará como um comando simples (Seção 3.2.1)
Um comando simples é o tipo de comando encontrado com mais frequência. É apenas uma sequência de palavras separadas por espaços em branco, terminadas por um dos operadores de controle do shell (consulte Definições). A primeira palavra geralmente especifica um comando a ser executado, com o restante das palavras sendo os argumentos desse comando.
A definição de espaços em branco (Seção 2 - Definições)
em branco Um caractere de espaço ou tabulação.
Por fim, o bash chama o comando interno echo (Seção 4.2 - Comandos internos do bash)
... Gera os argumentos, separados por espaços, terminados com uma nova linha. ...
Então, para resumir, as novas linhas são removidas pela Divisão de Palavras e, em seguida, o echo obtém 2 argumentos, "a" e "b" e os gera separados por espaços e terminando com uma nova linha.
Fazendo o que o @cyrus sugeriu (e suprima a nova linha do eco com -n), o resultado é melhor:
$ echo -n "$x" | xxd
0000000: 610a 62 a.b
Novas linhas no final da string
Ainda não é perfeito, a nova linha se foi. Olhando mais de perto a Substituição de Comando (Seção 3.5.4) :
O Bash executa a expansão executando o comando e substituindo a substituição do comando pela saída padrão do comando, com as novas linhas finais excluídas.
Agora que está claro por que a nova linha vai embora, o bash pode ser enganado para mantê-la. Para fazer isso, adicione uma sequência adicional ao final e remova-a ao usar a variável:
$ x=$( echo a; echo b; echo -n extra )
$ echo -n "${x%extra}" | xxd
0000000: 610a 620a a.b.
TL; DR
Adicione parte extra ao final da saída e cite variáveis:
$ 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