A atribuição não remove os caracteres de nova linha, é realmente echofazer isso. Você precisa simplesmente colocar aspas ao redor da string para manter essas novas linhas:
echo "$testvar"
Isso dará o resultado que você deseja. Veja a seguinte transcrição para uma demonstração:
pax> cat num1.txt ; x=$(cat num1.txt)
line 1
line 2
pax> echo $x ; echo '===' ; echo "$x"
line 1 line 2
===
line 1
line 2
A razão pela qual as novas linhas são substituídas por espaços não está inteiramente relacionada ao echocomando, mas sim uma combinação de coisas.
Quando fornecida uma linha de comando, bashdivide-a em palavras de acordo com a documentação da IFSvariável:
IFS: o separador de campo interno que é usado para divisão de palavras após a expansão ... o valor padrão é <space><tab><newline>.
Isso especifica que, por padrão, qualquer um desses três caracteres pode ser usado para dividir seu comando em palavras individuais. Depois disso, os separadores de palavras se foram, tudo o que resta é uma lista de palavras.
Combine isso com a echodocumentação (um bashcomando interno) e você verá por que os espaços são produzidos:
echo [-neE] [arg ...]: Produz os argumentos, separados por espaços, seguidos por uma nova linha.
Quando você usa echo "$x", força a xvariável inteira a ser uma única palavra de acordo com bash, portanto, não é dividida. Você pode ver isso com:
pax> function count {
...> echo $#
...> }
pax> count 1 2 3
3
pax> count a b c d
4
pax> count $x
4
pax> count "$x"
1
Aqui, a countfunção simplesmente imprime o número de argumentos fornecidos. As variantes 1 2 3e o a b c dmostram em ação.
Em seguida, tentamos com as duas variações da xvariável. A única sem citações mostra que existem quatro palavras, "test", "1", "test"e "2". Adicionar as aspas torna uma única palavra "test 1\ntest 2".