Como converter uma nova linha do DOS / Windows (CRLF) em Nova linha do Unix (LF) em um script Bash?

336

Como programaticamente (ou seja, não estou usando vi) converter novas linhas de DOS / Windows para Unix?

Os comandos dos2unixe unix2dosnão estão disponíveis em determinados sistemas. Como posso emular estes com comandos como sed/ awk/ tr?

Koran Molovik
fonte
9
Em geral, basta instalar dos2unixusando o gerenciador de pacotes, é realmente muito mais simples e existe na maioria das plataformas.
Brad Koch
11
Acordado! @BradKoch Simples como 'brew install dos2unix' no Mac OSX
SmileIT

Respostas:

322

Você pode usar trpara converter do DOS para o Unix; no entanto, você só poderá fazer isso com segurança se o CR aparecer no seu arquivo apenas como o primeiro byte de um par de bytes CRLF. Este é geralmente o caso. Você então usa:

tr -d '\015' <DOS-file >UNIX-file

Observe que o nome DOS-fileé diferente do nome UNIX-file; se você tentar usar o mesmo nome duas vezes, acabará sem dados no arquivo.

Você não pode fazer o contrário (com o padrão 'tr').

Se você souber inserir retorno de carro em um script ( control-V, control-Mpara inserir control-M), então:

sed 's/^M$//'     # DOS to Unix
sed 's/$/^M/'     # Unix to DOS

onde o '^ M' é o caractere control-M. Você também pode usar o mecanismo de bash cotação ANSI-C para especificar o retorno de carro:

sed $'s/\r$//'     # DOS to Unix
sed $'s/$/\r/'     # Unix to DOS

No entanto, se você precisar fazer isso com muita frequência (mais de uma vez, grosso modo), é muito mais sensato instalar os programas de conversão (por exemplo , dos2unixe unix2dos, ou talvez dtoue utod) e usá-los.

Se você precisar processar diretórios e subdiretórios inteiros, poderá usar zip:

zip -r -ll zipfile.zip somedir/
unzip zipfile.zip

Isso criará um arquivo zip com as terminações de linha alteradas de CRLF para CR. unzipEm seguida, os arquivos convertidos serão restaurados (e você solicitará arquivo por arquivo - você poderá responder: Sim para todos). Créditos para @vmsnomad por apontar isso.

Jonathan Leffler
fonte
9
usando tr -d '\015' <DOS-file >UNIX-filewhere DOS-file== UNIX-fileapenas resulta em um arquivo vazio. O arquivo de saída deve ser um arquivo diferente, infelizmente.
Buttle Butkus
3
@ButtleButkus: Bem, sim; por isso usei dois nomes diferentes. Se você zapear o arquivo de entrada antes que o programa leia tudo, como quando você usa o mesmo nome duas vezes, você termina com um arquivo vazio. Esse é um comportamento uniforme em sistemas do tipo Unix. Requer código especial para lidar com a substituição segura de um arquivo de entrada. Siga as instruções e você estará bem.
Jonathan Leffler
Parece que me lembro da funcionalidade de substituição de pesquisa no arquivo em algum lugar.
precisa
4
Existem lugares; você tem que saber onde encontrá-los. Dentro dos limites, a sedopção GNU -i(no local) funciona; os limites são arquivos vinculados e links simbólicos. O sortcomando 'always' (desde 1979, se não for anterior) suportou a -oopção que pode listar um dos arquivos de entrada. No entanto, isso é em parte porque sortdeve ler todas as suas entradas antes que possa gravar qualquer uma de suas saídas. Outros programas suportam esporadicamente a substituição de um de seus arquivos de entrada. Você pode encontrar um programa de uso geral (script) para evitar problemas no 'The UNIX Programming Environment' da Kernighan & Pike.
Jonathan Leffler
3
A terceira opção funcionou para mim, obrigado. Eu usei a opção -i: sed -i $'s/\r$//' filename- para editar no local. Como estou trabalhando em uma máquina que não tem acesso à Internet, a instalação do software é um problema.
Warren Orvalho
64
tr -d "\r" < file

veja aqui exemplos usando sed:

# IN UNIX ENVIRONMENT: convert DOS newlines (CR/LF) to Unix format.
sed 's/.$//'               # assumes that all lines end with CR/LF
sed 's/^M$//'              # in bash/tcsh, press Ctrl-V then Ctrl-M
sed 's/\x0D$//'            # works on ssed, gsed 3.02.80 or higher

# IN UNIX ENVIRONMENT: convert Unix newlines (LF) to DOS format.
sed "s/$/`echo -e \\\r`/"            # command line under ksh
sed 's/$'"/`echo \\\r`/"             # command line under bash
sed "s/$/`echo \\\r`/"               # command line under zsh
sed 's/$/\r/'                        # gsed 3.02.80 or higher

Use sed -ipara conversão no local, por exemplo sed -i 's/..../' file.

ghostdog74
fonte
10
Eu usei uma variante, já que meu arquivo só tinha \r:tr "\r" "\n" < infile > outfile
Matt Todd
11
@MattTodd você poderia postar isso como uma resposta? o -dé apresentado com mais frequência e não ajudará na \rsituação " única ".
N611x007
5
Observe que o proposto \rao \nmapeamento tem o efeito de espaçamento duplo dos arquivos; cada linha CRLF que termina no DOS se torna \n\nno Unix.
precisa
Posso fazer isso recursivamente?
Aaron Franke
36

Fazer isso com o POSIX é complicado:

  • O POSIX Sed não suporta \rou \15. Mesmo se tiver, a opção no local -inão é POSIX

  • O POSIX Awk suporta \re \15, no entanto, a -i inplaceopção não é POSIX

  • O d2u e o dos2unix não são utilitários POSIX , mas ex é

  • POSIX ex não suporta \r, \15, \nou\12

Para remover retornos de carro:

ex -bsc '%!awk "{sub(/\r/,\"\")}1"' -cx file

Para adicionar retornos de carro:

ex -bsc '%!awk "{sub(/$/,\"\r\")}1"' -cx file
Steven Penny
fonte
2
Parece que o POSIX trsuporta \r. Portanto, você também pode usar printf '%s\n' '%!tr -d "\r"' x | ex file(embora concedido, isso foi removido, \rmesmo que não imediatamente anterior \n). Além disso, a -bopção para exnão é especificada pelo POSIX.
Curinga
11
Fazer isso no POSIX é fácil. Incorpore o literal CR no script digitando-o (é control-M).
Joshua
28

Você pode usar o vim programaticamente com a opção -c {command}:

Dos para Unix:

vim file.txt -c "set ff=unix" -c ":wq"

Unix para dos:

vim file.txt -c "set ff=dos" -c ":wq"

"definir ff = unix / dos" significa alterar o formato do arquivo (ff) do arquivo para o formato final de linha do Unix / DOS

": wq" significa gravar o arquivo no disco e sair do editor (permitindo usar o comando em um loop)

Johan Zicola
fonte
3
Essa parecia a solução mais elegante, mas a falta de explicação sobre o que significa wq é lamentável.
Jorrick Sleijster 23/02/19
4
Quem usa visabe o que :wqsignifica. Para aqueles que não os 3 caracteres significam 1) abra a área de comando vi, 2) escreva e 3) saia.
David Newcomb
Eu não tinha idéia que você poderia interativamente adicionar comandos para vim do CLI
Robert Dundon
você pode usar ": x" em vez de ": wq"
Joseph Conrad
25

Usando o AWK, você pode:

awk '{ sub("\r$", ""); print }' dos.txt > unix.txt

Usando Perl você pode fazer:

perl -pe 's/\r$//' < dos.txt > unix.txt
codaddict
fonte
2
Uma solução agradável e portátil awk .
mklement0
23

Para converter um arquivo no local, use

dos2unix <filename>

Para gerar texto convertido para um arquivo diferente, use

dos2unix -n <input-file> <output-file>

Você pode instalá-lo no Ubuntu ou Debian com

sudo apt install dos2unix

ou no macOS usando homebrew

brew install dos2unix
Boris
fonte
11
Eu sei que a pergunta pede alternativas ao dos2unix, mas é o primeiro resultado do google.
Boris
18

Esse problema pode ser resolvido com ferramentas padrão, mas existem muitos traps para os incautos que eu recomendo que você instale o flipcomando, que foi escrito há mais de 20 anos por Rahul Dhesi, autor de zoo. Ele faz um excelente trabalho na conversão de formatos de arquivo e, por exemplo, evita a destruição inadvertida de arquivos binários, o que é um pouco fácil demais se você apenas alterar todos os CRLF que vê ...

Norman Ramsey
fonte
Alguma maneira de fazer isso de maneira streaming, sem modificar o arquivo original?
Augurar
@augurar você pode verificar "pacotes similares" packages.debian.org/wheezy/flip
n611x007
Tive a experiência de quebrar metade do meu sistema operacional apenas executando o texxto com um sinalizador errado. Tenha cuidado, especialmente se você quiser fazê-lo em pastas inteiras.
A_P
14

As soluções postadas até agora tratam apenas de parte do problema, convertendo o CRLF do DOS / Windows no LF do Unix; a parte que falta é que o DOS use CRLF como separador de linhas , enquanto o Unix use LF como terminador de linhas . A diferença é que um arquivo DOS (geralmente) não terá nada após a última linha do arquivo, enquanto o Unix terá. Para fazer a conversão corretamente, você precisa adicionar o LF final (a menos que o arquivo tenha tamanho zero, ou seja, não possui linhas). Meu encantamento favorito para isso (com um pouco de lógica adicional para lidar com arquivos separados por CR no estilo Mac, e não molestar arquivos que já estão no formato unix) é um pouco de perl:

perl -pe 'if ( s/\r\n?/\n/g ) { $f=1 }; if ( $f || ! $m ) { s/([^\n])\z/$1\n/ }; $m=1' PCfile.txt

Observe que isso envia a versão Unixified do arquivo para stdout. Se você deseja substituir o arquivo por uma versão Unixified, adicione o -isinalizador do perl .

Gordon Davisson
fonte
@LudovicZenohateLagouardette Era um arquivo de texto simples (ou seja, csv ou texto com demissão por tabulação) ou algo mais? Se estava em algum formato ish de banco de dados, é muito provável que manipulá-lo como se fosse um texto que corrompa sua estrutura interna.
Gordon Davisson 23/01
Um texto simples csv, mas acho que o encontro foi estranho. Eu acho que errei por causa disso. No entanto, não se preocupe. Estou sempre coletando backups e esse nem era o conjunto de dados real, apenas um de 1GB. O real é um 26gb.
Ludovic Zenohate Lagouardette
14

Se você não tem acesso ao dos2unix , mas pode ler esta página, pode copiar / colar o dos2unix.py daqui.

#!/usr/bin/env python
"""\
convert dos linefeeds (crlf) to unix (lf)
usage: dos2unix.py <input> <output>
"""
import sys

if len(sys.argv[1:]) != 2:
  sys.exit(__doc__)

content = ''
outsize = 0
with open(sys.argv[1], 'rb') as infile:
  content = infile.read()
with open(sys.argv[2], 'wb') as output:
  for line in content.splitlines():
    outsize += len(line) + 1
    output.write(line + '\n')

print("Done. Saved %s bytes." % (len(content)-outsize))

Postada cruzada do superusuário .

anatoly techtonik
fonte
11
O uso é enganoso. O real dos2unixconverte todos os arquivos de entrada por padrão. Seu uso implica -nparâmetro. E o real dos2unixé um filtro que lê stdin, grava no stdout se os arquivos não forem fornecidos.
JFS
8

Super fácil com PCRE;

Como um script ou substitua $@pelos seus arquivos.

#!/usr/bin/env bash
perl -pi -e 's/\r\n/\n/g' -- $@

Isso substituirá seus arquivos no lugar!

Eu recomendo fazer isso apenas com um backup (controle de versão ou não)

ThorSummoner
fonte
Obrigado! Isso funciona, embora eu esteja escrevendo o nome do arquivo e não --. Eu escolhi essa solução porque é fácil de entender e se adaptar para mim. FYI, este é o que os interruptores fazer: -passumir um loop "enquanto a entrada", -iarquivo de entrada de edição no local, -eexecutar seguinte comando
Rolf
Estritamente falando, o PCRE é uma reimplementação do mecanismo de regex do Perl, não o mecanismo de regex do Perl. Ambos têm essa capacidade, embora também existam diferenças, apesar da provocação no nome.
Tripleee 27/10
6

Uma solução awk ainda mais simples sem um programa:

awk -v ORS='\r\n' '1' unix.txt > dos.txt

Tecnicamente '1' é o seu programa, o b / c awk requer um quando é dada a opção.

ATUALIZAÇÃO : Depois de revisitar esta página pela primeira vez em muito tempo, percebi que ninguém ainda havia publicado uma solução interna, então aqui está uma:

while IFS= read -r line;
do printf '%s\n' "${line%$'\r'}";
done < dos.txt > unix.txt
nawK
fonte
Isso é útil, mas só para ficar claro: isso traduz o Unix -> Windows / DOS, que é a direção oposta à solicitada pelo OP.
mklement0
5
Foi feito de propósito, deixado como um exercício para o autor. eyerolls awk -v RS='\r\n' '1' dos.txt > unix.txt
nawK
Ótimo (e parabéns a você pela delicadeza pedagógica).
precisa saber é o seguinte
11
msgstr "b / c awk requer uma opção quando dada." - O awk sempre requer um programa, independentemente de as opções serem especificadas ou não.
precisa saber é o seguinte
11
A solução pura do bash é interessante, mas muito mais lenta que um equivalente awkou sedsolução. Além disso, você deve usar while IFS= read -r linepara preservar fielmente as linhas de entrada, caso contrário, os espaços em branco à esquerda e à direita são aparados (como alternativa, não use nenhum nome de variável no readcomando e trabalhe com $REPLY).
precisa saber é o seguinte
5

Curiosamente no meu git-bash no Windows sed ""já fiz o truque:

$ echo -e "abc\r" >tst.txt
$ file tst.txt
tst.txt: ASCII text, with CRLF line terminators
$ sed -i "" tst.txt
$ file tst.txt
tst.txt: ASCII text

Meu palpite é que o sed os ignora ao ler as linhas da entrada e sempre escreve finais de linha unix na saída.

user829755
fonte
4

Isso funcionou para mim

tr "\r" "\n" < sampledata.csv > sampledata2.csv 
Santosh
fonte
9
Isto irá converter cada única DOS-nova linha em dois UNIX novas linhas.
Melebius 4/08/15
4

Só tive que refletir sobre a mesma pergunta (no lado do Windows, mas igualmente aplicável ao Linux.) Surpreendentemente, ninguém mencionou uma maneira muito automatizada de fazer a conversão CRLF <-> LF para arquivos de texto usando a boa e velha zip -llopção (Info-ZIP):

zip -ll textfiles-lf.zip files-with-crlf-eol.*
unzip textfiles-lf.zip 

NOTA: isso criaria um arquivo zip preservando os nomes originais, mas convertendo as terminações de linha em LF. Em seguida unzip, extrairia os arquivos como zipados, ou seja, com seus nomes originais (mas com terminações LF), solicitando a substituição dos arquivos originais locais, se houver.

Trecho relevante de zip --help:

zip --help
...
-l   convert LF to CR LF (-ll CR LF to LF)
vmsnomad
fonte
Melhor resposta, de acordo com mim, pois pode processar diretórios e subdiretórios inteiros. Estou feliz por ter cavado tão longe.
caram 9/03
2

Para o Mac osx, se você tiver o homebrew instalado [ http://brew.sh/{/1]

brew install dos2unix

for csv in *.csv; do dos2unix -c mac ${csv}; done;

Verifique se você fez cópias dos arquivos, pois este comando modificará os arquivos no local. A opção -c mac torna o switch compatível com osx.

Ashley Raiteri
fonte
Essa resposta realmente não é a pergunta do pôster original.
hlin117
2
Os usuários do OS X não devem usar -c mac, o que é para converter CRapenas novas linhas anteriores ao OS X. Você deseja usar esse modo apenas para arquivos de e para o Mac OS 9 ou anterior.
askewchan
2

TIMTOWTDI!

perl -pe 's/\r\n/\n/; s/([^\n])\z/$1\n/ if eof' PCfile.txt

Baseado em @GordonDavisson

É preciso considerar a possibilidade de [noeol]...

lzc
fonte
2

Você pode usar o awk. Defina o separador de registro ( RS) como uma expressão regular que corresponda a todos os caracteres ou caracteres de nova linha possíveis. E defina o separador de registro de saída ( ORS) como o caractere de nova linha no estilo unix.

awk 'BEGIN{RS="\r|\n|\r\n|\n\r";ORS="\n"}{print}' windows_or_macos.txt > unix.txt
kazmer
fonte
Isso é o que funcionou para mim (MacOS, git diffmostra ^ M, editado no vim)
Dorian
2

No Linux, é fácil converter ^ M (ctrl-M) em * nix novas linhas (^ J) com sed.

Será algo assim na CLI, haverá realmente uma quebra de linha no texto. No entanto, o \ passa esse ^ J para sed:

sed 's/^M/\
/g' < ffmpeg.log > new.log

Você obtém isso usando ^ V (ctrl-V), ^ M (ctrl-M) e \ (barra invertida) enquanto digita:

sed 's/^V^M/\^V^J/g' < ffmpeg.log > new.log
jato
fonte
isso é o que funcionou para mim, obrigado!
Dan Mantyla
2
sed --expression='s/\r\n/\n/g'

Como a pergunta menciona o sed, esta é a maneira mais direta de usar o sed para conseguir isso. O que a expressão diz é substituir todo retorno de carro e avanço de linha por apenas apenas avanço de linha. É disso que você precisa quando passa do Windows para o Unix. Eu verifiquei que funciona.

John Paul
fonte
Olá, John Paul - esta resposta foi sinalizada para exclusão, então surgiu em uma fila de revisão para mim. Em geral, quando você tem uma pergunta como essa com 8 anos de idade e 22 respostas, você deve explicar como sua resposta é útil de uma maneira que outras respostas existentes não são.
Zzxyz 18/10/19
0

Como uma extensão da solução Unix para DOS de Jonathan Leffler, para converter com segurança para o DOS quando você não tiver certeza das terminações de linha atuais do arquivo:

sed '/^M$/! s/$/^M/'

Isso verifica se a linha ainda não termina em CRLF antes de converter em CRLF.

Gannet
fonte
0

Eu criei um script com base na resposta aceita para que você possa convertê-lo diretamente sem precisar de um arquivo adicional no final e remover e renomear posteriormente.

convert-crlf-to-lf() {
    file="$1"
    tr -d '\015' <"$file" >"$file"2
    rm -rf "$file"
    mv "$file"2 "$file"
}

apenas verifique se você possui um arquivo como "file1.txt" que "file1.txt2" ainda não exista ou será substituído, use-o como um local temporário para armazenar o arquivo.

OZZIE
fonte
0

Com o bash 4.2 e mais recente, você pode usar algo parecido com isto para remover o CR à direita, que usa apenas os built-ins do bash:

if [[ "${str: -1}" == $'\r' ]]; then
    str="${str:: -1}"
fi
glevand
fonte