Ao imprimir uma variável que contém novas linhas, por que a última nova linha é removida?

Ao imprimir uma variável que contém novas linhas, por que a última nova linha é removida?

Conteúdo do arquivo.txt (sem estranheza, arquivo de texto conforme definido pelo POSIX)

iguana
gecko
anole

Roteiro de exemplo:

#!/bin/sh

string="$(cat file.txt)"

printf '%s' "$string"

Exemplo de saída:

[coolguy@somemachine ~]$ ./script.sh
iguana
gecko
anole[coolguy@somemachine ~]$

O que aconteceu com a última nova linha? Por que todas as novas linhas, exceto a última, são preservadas? Parece que não deveríamos usar echo para adicionar uma nova linha se já existisse uma lá.

Responder1

Não é a impressão, é a substituição do comando que faz isso. Está definido para fazer isso. DeDescrição POSIX:

O shell deve expandir a substituição do comando executando o comando em um ambiente subshell e substituindo a substituição do comando pela saída padrão do comando,removendo sequências de um ou mais caracteres {newline} no final da substituição.

Observe que ele removetodosnovas linhas à direita, não apenas uma.

Em um caso um tanto comum, você usaria a substituição de comando para capturar uma saída de uma linha, digamos osrev=$(uname -r). Os utilitários geralmente imprimem uma nova linha final, para conveniência do usuário na linha de comando. Mas em um script de shell, você pode querer usar essa string como parte de outra, digamos, um nome de arquivo: filename=blahblah-$osrev.dat. E nesse caso, a nova linha final seria apenas um incômodo.

E, claro, um plano echoadicionará uma nova linha final em qualquer caso.


Se você deseja que o conteúdo do arquivo esteja na variável, a solução alternativa comum é adicionar um caractere extra na substituição do comando e removê-lo posteriormente:

printf "foo\nbar\n\n" > file
string=$(cat file; echo x)
string=${string%x}
printf '%q\n' "$string"

que gera $'foo\nbar\n\n', mostrando ambas as novas linhas finais presentes.


Dependendo do que você pretende fazer com os dados, pode haver outras formas. Por exemplo, um while readloop, ou Bash mapfile, se você quiser processar o arquivo linha por linha.

informação relacionada