Trate "\ r" como nada em "while read -r"

1

Eu tenho essa linha de código que lê um arquivo de texto linha por linha.

O arquivo de texto às vezes é gerado por um usuário do Windows, às vezes por um usuário Unix. Portanto, às vezes, vejo \r\n no final da linha e, às vezes, vejo apenas \n .

Eu quero que meu script consiga lidar com os dois cenários e alcançar cada linha separadamente, independentemente de o quebra de linha ser \r ou \n ou \r\n ou \n\r .

while read -r textFileLines; do ... something ...; done < text_file.txt

Este código funciona com \n\r (LF CR) no final de cada linha, mas NÃO funciona quando tenho \r\n no final do linha!

TESTE

  • Crie um novo arquivo de texto usando Notepad++ v7.5.4

  • whileread-rLINE;doecho"$LINE"; done < /cygdrive/d/test_text.txt

  • saída no terminal:

    first_line
    second_line
    third_string
    

Por que a linha fourth_output não é mostrada?

    
por vivoru 17.07.2018 / 15:37

4 respostas

0

Se você tiver alguns arquivos que são arquivos de texto do DOS e alguns que são arquivos de texto Unix, seu script pode passar todos os dados por dos2unix :

dos2unix <filename |
while IFS= read stuff; do
   # do things with "$stuff"
done 

Arquivos de texto Unix não seriam modificados por isso.

Para lidar adicionalmente com as quebras de linha do Mac, acredito que você possa fazer

dos2unix <filename | mac2unix |
while IFS= read stuff; do
   # do things with "$stuff"
done 

A última linha não é gerada pelo loop read , pois não é terminada e, portanto, não é uma linha.

Para detectar se um arquivo não tem nova linha de término na última linha e adicione um, se não tiver, em bash :

if [ "$( tail -c 1 filename )" != $'\n' ]; then
    printf '\n' >>filename
fi

Relacionados:

por 17.07.2018 / 16:13
1

Why isn't the fourth_output line not shown?

Na sua imagem, falta no arquivo a nova linha no final da última linha. read só retorna true se ler o delimitador (nova linha) e, como não está lá no final da última linha, read retorna falso, seu loop termina e a última linha incompleta não é impressa.

Isso não tem nada a ver com o retorno de carro, o comportamento é o mesmo, mesmo com apenas NL, se a última linha estiver faltando a NL.

Aqui, file1 tem duas linhas com finais de linha CRLF:

$ cat -A file1
foo^M$
bar^M$
$ while read x ; do echo "<$x>"; done < file1
>foo
>bar

file2 está faltando a linha que termina na segunda linha:

$ cat -A file2 ; echo
foo^M$
bar
$ while read x ; do echo "<$x>"; done < file2
>foo

Se você deseja que o loop também processe o fragmento de linha final, será necessário verificar se a variável read contém dados quando o read retorna falhas:

$ while read -r x || [ "$x" ] ; do echo "<$x>"; done < file2
>foo
<bar>

Se você quiser se livrar do CR, pode removê-lo dentro do loop, por exemplo, x=${x%$'\r'}; (no Bash / ksh / zsh) ou pré-processe o arquivo com tr -d '\r' ou dos2unix ou algo semelhante.

    
por 17.07.2018 / 16:30
0

Executar:

$ [ -n "$(tail -c1 infile)" ] && echo >> infile
$ sed 's/\r$\|^\r//g;s/\r/\n/g' infile | while IFS= read -r line
> do echo "$line" ; done
DOS       line
second     DOS
old  mac   line
new  mac   line
end\n\rreverse
linux      line
new linux  line

Todos os problemas foram resolvidos.

Descrição:

Para corrigir o último uso de nova linha ausente:

[ -n "$(tail -c1 infile)" ] && echo >> infile

O qual adicionará uma nova linha à direita apenas se necessário (não alterará um arquivo correto).

Em seguida, você pode converter

  • \r\n (estilo DOS) para \n (basta remover um \ r no final da linha)
  • \n\r (estilo DOS inválido?) para um \n (remover no início da linha)
  • e depois (com pares corrigidos) converta \r (antigo MAC) para \n

em apenas uma chamada de (GNU) sed com:

sed 's/\r$\|^\r//g;s/\r/\n/g' infile

Se o arquivo de texto é como este arquivo de teste:

$ cat infile
DOS       line
second     DOS
new  mac   line
end\n\rreverse
linux      line
new linux  line
no  end   line

$ cat -A infile
DOS       line^M$
second     DOS^M$
old  mac   line^Mnew  mac   line$
end\n\rreverse$
^Mlinux      line$
new linux  line$
no  end   line

$  od -An -tc infile
   D   O   S                               l   i   n   e  \r  \n
   s   e   c   o   n   d                       D   O   S  \r  \n
   o   l   d           m   a   c               l   i   n   e  \r
   n   e   w           m   a   c               l   i   n   e  \n
   e   n   d   \   n   \   r   r   e   v   e   r   s   e  \n  \r
   l   i   n   u   x                           l   i   n   e  \n
   n   e   w       l   i   n   u   x           l   i   n   e  \n
   n   o           e   n   d               l   i   n   e
    
por 20.07.2018 / 04:48
0

Existem ferramentas explícitas disponíveis para isso. o mais comum que pode ser usado para remover \r\n dos arquivos é chamado dos2unix .

Se isso não estiver disponível no seu sistema, você pode usar um dos seguintes comandos para fazer algo semelhante em relação à sua variável textFileLines :

awk
$ echo "$textFileLines" | awk 1 RS='\r\n' ORS=
sed 1
$ echo "$textFileLines" | sed -e 's/\r//g'
sed 2
$ echo $textFileLines | sed $'s/\r//'
tr
$ echo "$textFileLines" | tr -d '\r'

Existem muitas outras maneiras de fazer isso, estas são apenas algumas das mais comuns.

Referências

por 17.07.2018 / 15:57