curl / wget está adicionando um extra ^ M quando eu adiciono dados a um arquivo

1

Algo está me chamando disso. Eu estou tentando baixar dois arquivos de hosts diferentes em um, se eu fizer isso com segurança, então tudo está bem, mas quando eu acrescentar os primeiros ao segundo um caractere estranho ^M aparece em cada linha do arquivo host.

Para dar um exemplo real aqui o que estou fazendo

wget https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts -O /etc/hosts && curl -s "https://raw.githubusercontent.com/CHEF-KOCH/CKs-FilterList/master/HOSTS/CK's-Spotify-HOSTS-FilterList.txt" >> /etc/hosts

agora /etc/hosts tem estes:

masquandofaçoissoseparadamente,então

curl-s"https://raw.githubusercontent.com/CHEF-KOCH/CKs-FilterList/master/HOSTS/CK's-Spotify-HOSTS-FilterList.txt" > /tmp/hosts

agora /tmp/hosts é perfeitamente normal

Porqueissoestáacontecendo?Porquequandoeubaixarosarquivosseparadamenteeunãorecebooavançodelinhaerrado,masquandoeuoscombinoeuentendo.Ésupostamente0x0anão0x0a0x0d,porqueissoestáacontecendo?

Sevocêprecisardarumaolhadanosarquivosqueestãosendobaixados,váparaoslinksdoscomandos:

  1. link
  2. link

EDIT: Eu tentei acrescentar apenas o segundo arquivo host para um arquivo hosts mudo e o mesmo aconteceu, então podemos omitir que o primeiro arquivo é a causa do problema

    
por AK_ 19.06.2018 / 22:03

1 resposta

3

Nenhuma ferramenta está adicionando nada. É uma grande confusão (mas não é sua culpa) por causa de algumas razões.

Existem dois finais de linha comuns:

  • Estilo Unix, um caractere denotado LF (ou \n ou 0x0a ),
  • estilo Windows, dois caracteres, CRLF (ou \r\n ou 0x0d 0x0a ).

Você faz o download de dois URLs diferentes. Parece que o servidor afirma que cada arquivo é text/plain , então eles devem usar CRLF . A segunda (a que você usa curl ) usa CRLF , mas a primeira (a que você usa wget ) usa ilegalmente a LF .

Se você fizer o download apenas do primeiro URL (não importa se com wget ou curl ) e armazenar o resultado em um arquivo hosts1 , file hosts1 produzirá:

hosts1: UTF-8 Unicode text

(Isso significa que os finais de linha são LF , caso contrário, seria UTF-8 Unicode text, with CRLF line terminators ).

Se você fizer o download apenas do segundo URL e armazenar o resultado em um arquivo hosts2 , file hosts2 produzirá:

hosts2: ASCII text, with CRLF line terminators

Se você fizer o download de ambos para o mesmo arquivo (digamos hosts12 ), obterá LF como terminações de linha para linhas que vieram do primeiro URL e CRLF como finais de linha para linhas que veio da segunda URL.

Na prática, qualquer ferramenta que tente dizer se um arquivo usa LF ou CRLF examina no máximo algumas linhas iniciais, nem todas elas. Experimente file hosts12 e você terá:

hosts12: UTF-8 Unicode text

exatamente como era para hosts1 . O mesmo acontece quando você vim hosts12 : o editor detecta finais de linha como LF com base no início do arquivo. Então você pula para o final e vê muitos ^M -s que denotam CR caracteres. vim imprime porque não considera CR como parte da linha correta que termina neste caso.

No entanto, quando você vim hosts2 , o editor detecta corretamente os finais de linha como CRLF . Os mesmos caracteres CR que foram impressos como ^M anterior, agora estão ocultos de você porque vim os considera partes de finais de linha adequados. Se você adicionasse uma nova linha manualmente, vim usaria a linha no estilo Windows terminando mesmo se você estivesse no Unix. Você pode achar que o arquivo é "perfeitamente normal", mas não é um arquivo de texto normal do Unix.

A confusão é porque os dois arquivos no servidor usam diferentes terminações de linha; então vim tenta ser inteligente.

No Linux (Unix em geral), você deseja que seu /etc/hosts use LF como terminações de linha. Veja as definições POSIX da linha e caractere de nova linha . É explicitamente declarado que o caractere é \n :

3.243 Newline Character (<newline>)
A character that in the output stream indicates that printing should start at the beginning of the next line. It is the character designated by '\n' in the C language.

Eu não acho que as ferramentas são obrigadas a suportar \r\n . A solução simples é executar wget … && curl … >> … exatamente como você fez e invocar dos2unix /etc/hosts .

Se eu fosse você, trabalharia com outro arquivo, digamos /etc/hosts.tmp . Eu usaria wget , curl , dos2unix , chmod --reference=/etc/hosts , chown --reference=/etc/hosts . Somente quando o arquivo estiver completo, eu usaria mv para substituir /etc/hosts . Esta funcionalidade de rename(2) é relevante:

If newpath already exists, it will be atomically replaced, so that there is no point at which another process attempting to access newpath will find it missing.

Assim, qualquer processo encontraria o antigo /etc/hosts (antes de mv ) ou o novo (após mv ). Sua abordagem atual, trabalhando diretamente com /etc/hosts , permite cenários em que outro processo encontra o arquivo incompleto ou com finais de linha errados próximos do fim.

    
por 20.06.2018 / 23:27