Cite sua variável:
echo "$line" >> /tmp/dest_file
Estou tendo um problema, preciso copiar o conteúdo do arquivo e remover algumas linhas se elas corresponderem à saída de um comando anterior. Mas até agora, estou tendo um problema em manter as linhas de arquivo exatamente iguais. Eu estou colocando a parte simples do script como o if para omitir a cópia não fazem parte do problema, pois isso acontece com linhas não afetadas.
Para o exemplo:
No arquivo original eu tenho o seguinte
Testing, resuming text
Quando executar o script, os campos se tornam:
Testing, resuming text
Estou fazendo o seguinte:
#!/usr/bin/bash
rm /tmp/dest_file
while read line
do
echo $line >> /tmp/dest_file
done < $1
O problema que tenho com isso é que os arquivos se tornarão diferentes devido aos campos formatados da guia.
Isso já foi feito várias vezes neste site - consulte Noções básicas sobre o IFS e as perguntas vinculadas. Nesta resposta, vou resumir o que pode dar errado e como evitá-lo; veja os tópicos ligados para detalhes.
read line
realiza as seguintes ações:
line
. \
se torna uma única barra invertida. Em outras palavras, a barra invertida cita o próximo caractere, desde que não seja uma nova linha. read
parar em uma nova linha e o caractere no final da linha for um \
, remova a sequência da nova linha invertida e continue lendo, anexando à variável line
. Repita até o primeiro: uma nova linha que não seja precedida por uma barra invertida; um byte nulo; o final da entrada. line
que é feito de caracteres em $IFS
. Por padrão, IFS
contém uma guia, um espaço e uma nova linha, portanto, isso separa os espaços em branco ASCII do final do valor de line
. line
que é feito de caracteres de espaço em branco em $IFS
. Por exemplo, se a entrada for
: hello\
world: :
wibble
, em seguida, read line
resulta em line
contendo : helloworld: :
(sem espaço inicial) com o valor padrão de IFS
. Se IFS
tiver sido alterado para :
(apenas dois pontos), então read line
resultará em : helloworld:
(com um espaço no início e no final). Se IFS
contiver :
e um espaço, o resultado será : helloworld
(sem espaço inicial ou final).
Para evitar a influência de IFS
, defina-o como um valor vazio (observe que isso é diferente de desativá-lo). Você pode configurá-lo apenas para o comando read
escrevendo IFS= read
(consulte Por que é 'enquanto IFS = read' é usado com tanta frequência, ao invés de 'IFS =; while read ..'? ).
Para evitar o processamento de contrabarra, passe a opção -r
para read
.
A menos que o shell seja zsh, se houver um byte nulo na entrada, os caracteres subseqüentes serão perdidos. Os shells não são projetados para ler dados binários.
Assim, o idioma para ler uma linha por vez é:
while IFS= read -r line; do
… # process "$line"
end
Quando você usa a variável line
, certifique-se de que sempre coloque aspas duplas em torno das substituições de variáveis : "$line"
. Sem aspas duplas, o shell primeiro expande o valor da variável e, em seguida, divide esse valor em palavras separadas, sempre que contiver caracteres de IFS
, e cada palavra é interpretada como um padrão curinga e substituída pela lista de arquivos correspondentes (se não há arquivos correspondentes, o padrão é deixado como está). Portanto, echo 'a* b*' | IFS= read -r line; echo $line
expande para a lista de arquivos no diretório atual, começando com a
ou b
; para obter a entrada inalterada, use echo 'a* b*' | IFS= read -r line; echo "$line"
.
Observe também que o comando echo
às vezes modifica a sequência impressa. O caminho exato depende do shell. Alguns shells processam escapes de barra invertida e alguns shells reconhecem as opções. Usar echo
para gerar uma string textual só funciona, você sabe que a string não contém nenhuma barra invertida e não inicia com um traço ( -
). Uma maneira confiável e portátil de imprimir uma string é
printf '%s\n' "$line"
Isso imprime uma nova linha após a string, como echo
. Você pode omitir a nova linha omitindo \n
no comando acima.