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

9

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

iguana
gecko
anole

Exemplo de script:

#!/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 são novas linhas, exceto a última preservada? Parece que não deveríamos ter que usar o echo para adicionar uma nova linha se já houvesse uma lá.

    
por Harold Fischer 30.05.2018 / 22:55

1 resposta

24

Não é a impressão, é a substituição de comando que faz isso. Está definido para fazer isso. A partir da descrição do POSIX :

The shell shall expand the command substitution by executing command in a subshell environment and replacing the command substitution with the standard output of the command, removing sequences of one or more {newline} characters at the end of the substitution.

Note que remove todas novas linhas iniciais, não apenas uma.

Em um caso 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 à direita, para conveniência do usuário na linha de comando. Mas em um shell script, 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 seria apenas um incômodo.

E, claro, um echo simples adicionará uma nova linha final em qualquer caso.

Se você quiser o conteúdo do arquivo como está na variável, a solução alternativa é 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 as novas linhas finais presentes.

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

    
por 30.05.2018 / 23:06