¿Cómo convertir DOS / Windows nueva línea (CRLF) a Unix nueva línea (LF) en un script Bash?

336

¿Cómo puedo viconvertir programáticamente (es decir, no usar ) nuevas líneas de DOS / Windows a Unix?

Los comandos dos2unixy unix2dosno están disponibles en ciertos sistemas. ¿Cómo puedo emularlos con comandos como sed/ awk/ tr?

Koran Molovik
fuente
99
En general, solo instale dos2unixusando su administrador de paquetes, realmente es mucho más simple y existe en la mayoría de las plataformas.
Brad Koch
1
¡Convenido! @BradKoch Simple como 'brew install dos2unix' en Mac OSX
SmileIT

Respuestas:

323

Puede usar trpara convertir de DOS a Unix; sin embargo, solo puede hacerlo de manera segura si CR aparece en su archivo solo como el primer byte de un par de bytes CRLF. Este suele ser el caso. Luego usas:

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

Tenga en cuenta que el nombre DOS-filees diferente del nombre UNIX-file; Si intenta usar el mismo nombre dos veces, terminará sin datos en el archivo.

No puede hacerlo al revés (con el estándar 'tr').

Si sabe cómo ingresar el retorno de carro en un script ( control-V, control-Mpara ingresar control-M), entonces:

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

donde '^ M' es el carácter control-M. También puede usar el mecanismo de bash cotización ANSI-C para especificar el retorno de carro:

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

Sin embargo, si va a tener que hacer esto con mucha frecuencia (más de una vez, en términos generales), es mucho más sensato instalar los programas de conversión (por ejemplo , dos2unixy unix2dos, o quizás dtouy utod) y usarlos.

Si necesita procesar directorios y subdirectorios completos, puede usar zip:

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

Esto creará un archivo zip con finales de línea cambiados de CRLF a CR. unzipluego volverá a colocar los archivos convertidos en su lugar (y le preguntará archivo por archivo; puede responder: Sí a todo). Créditos a @vmsnomad por señalar esto.

Jonathan Leffler
fuente
99
usando tr -d '\015' <DOS-file >UNIX-filewhere DOS-file== UNIX-filesolo da como resultado un archivo vacío. El archivo de salida tiene que ser un archivo diferente, desafortunadamente.
Buttle Butkus
3
@ButtleButkus: Bueno, sí; Por eso usé dos nombres diferentes. Si elimina el archivo de entrada antes de que el programa lo lea todo, como lo hace cuando usa el mismo nombre dos veces, termina con un archivo vacío. Ese es un comportamiento uniforme en sistemas tipo Unix. Requiere un código especial para manejar la sobrescritura de un archivo de entrada de forma segura. Siga las instrucciones y estará bien.
Jonathan Leffler
Parece que recuerdo alguna vez la funcionalidad de reemplazo de búsqueda en el archivo.
Buttle Butkus
44
Hay lugares; tienes que saber dónde encontrarlos. Dentro de los límites, la sedopción GNU -i(para el lugar) funciona; Los límites son archivos vinculados y enlaces simbólicos. El sortcomando 'siempre' (desde 1979, si no antes) admitió la -oopción que puede enumerar uno de los archivos de entrada. Sin embargo, eso es en parte porque sortdebe leer toda su entrada antes de que pueda escribir cualquiera de sus resultados. Otros programas admiten esporádicamente sobrescribir uno de sus archivos de entrada. Puede encontrar un programa de propósito general (script) para evitar problemas en 'El entorno de programación UNIX' de Kernighan & Pike.
Jonathan Leffler
3
La tercera opción funcionó para mí, gracias. Utilicé la opción -i: sed -i $'s/\r$//' filename- para editar en su lugar. Estoy trabajando en una máquina que no tiene acceso a Internet, por lo que la instalación del software es un problema.
Warren Dew
64
tr -d "\r" < file

mira aquí para ejemplos 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

Uso sed -ipara la conversión en el lugar, por ejemplo sed -i 's/..../' file.

ghostdog74
fuente
10
Usé una variante ya que mi archivo solo tenía \r:tr "\r" "\n" < infile > outfile
Matt Todd
1
@MattTodd, ¿podría publicar esto como respuesta? el -dse ofrece con más frecuencia y no ayudará en la "única \r" situación.
n611x007
55
Tenga en cuenta que la propuesta \rde \nmapeo tiene el efecto de doble espacio entre los archivos; cada línea CRLF que termina en DOS se convierte \n\nen Unix.
Jonathan Leffler
¿Puedo hacer esto de forma recursiva?
Aaron Franke el
36

Hacer esto con POSIX es complicado:

  • POSIX Sed no es compatible con \ro \15. Incluso si lo hiciera, la opción en el lugar -ino es POSIX

  • POSIX Awk es compatible \ry \15, sin embargo, la -i inplaceopción no es POSIX

  • d2u y dos2unix no son utilidades POSIX , pero ex es

  • POSIX ex no soporta \r, \15, \no\12

Para eliminar retornos de carro:

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

Para agregar retornos de carro:

ex -bsc '%!awk "{sub(/$/,\"\r\")}1"' -cx file
Steven Penny
fuente
2
Parece que POSIX es trcompatible \r. Por lo tanto, también puede usar printf '%s\n' '%!tr -d "\r"' x | ex file(aunque está garantizado, esto se elimina \rincluso si no precede inmediatamente \n). Además, POSIX no especifica la -bopción a ex.
Comodín
1
Hacer esto en POSIX es fácil. Incruste el literal CR en el script escribiéndolo (es control-M).
Joshua
28

Puede usar vim mediante programación con la opción -c {comando}:

Dos a Unix:

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

Unix a dos:

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

"set ff = unix / dos" significa cambiar el formato de archivo (ff) del archivo al formato de final de línea Unix / DOS

": wq" significa escribir el archivo en el disco y salir del editor (permitiendo usar el comando en un bucle)

Johan Zicola
fuente
3
Esta parecía la solución más elegante, pero la falta de explicación sobre lo que significa wq es lamentable.
Jorrick Sleijster
55
Cualquiera que use visabrá lo que :wqsignifica. Para aquellos que no lo hacen, los 3 caracteres significan 1) abrir el área de comando vi, 2) escribir y 3) salir.
David Newcomb
No tenía idea de que podía agregar comandos interactivamente a vim desde la CLI
Robert Dundon
puede usar ": x" en lugar de ": wq"
JosephConrad
25

Usando AWK puedes hacer:

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

Usando Perl puedes hacer:

perl -pe 's/\r$//' < dos.txt > unix.txt
codictorio
fuente
2
Una buena solución portátil awk .
mklement0
24

Para convertir un archivo en su lugar, use

dos2unix <filename>

Para enviar el texto convertido a un archivo diferente, use

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

Puede instalarlo en Ubuntu o Debian con

sudo apt install dos2unix

o en macOS usando homebrew

brew install dos2unix
Boris
fuente
1
Sé que la pregunta pide alternativas a dos2unix, pero es el primer resultado de Google.
Boris
18

Este problema se puede resolver con herramientas estándar, pero hay suficientes trampas para los incautos que recomiendo que instale el flipcomando, que fue escrito hace más de 20 años por Rahul Dhesi, el autor de zoo. Hace un excelente trabajo al convertir formatos de archivo y, por ejemplo, evita la destrucción accidental de archivos binarios, lo cual es un poco demasiado fácil si simplemente corres alterando cada CRLF que ves ...

Norman Ramsey
fuente
¿Alguna forma de hacer esto de forma continua, sin modificar el archivo original?
augurar
@augurar puede consultar "paquetes similares" packages.debian.org/wheezy/flip
n611x007
Tuve la experiencia de romper la mitad de mi sistema operativo simplemente ejecutando texxto con una bandera incorrecta. Tenga cuidado, especialmente si desea hacerlo en carpetas enteras.
A_P
14

Las soluciones publicadas hasta ahora solo abordan parte del problema, convirtiendo el CRLF de DOS / Windows en LF de Unix; la parte que les falta es que DOS usa CRLF como separador de línea , mientras que Unix usa LF como terminador de línea . La diferencia es que un archivo DOS (generalmente) no tendrá nada después de la última línea del archivo, mientras que Unix sí. Para realizar la conversión correctamente, debe agregar ese LF final (a menos que el archivo tenga una longitud cero, es decir, no tenga líneas). Mi encantamiento favorito para esto (con una pequeña lógica agregada para manejar archivos separados por CR de estilo Mac, y no molestar a los archivos que ya están en formato unix) es un poco perl:

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

Tenga en cuenta que esto envía la versión Unixified del archivo a stdout. Si desea reemplazar el archivo con una versión Unixified, agregue la -ibandera de perl .

Gordon Davisson
fuente
@LudovicZenohateLagouardette ¿Era un archivo de texto sin formato (es decir, csv o texto con tabulación), o algo más? Si estaba en algún formato de base de datos, manipularlo como si fuera texto es muy probable que corrompa su estructura interna.
Gordon Davisson el
Un texto sin formato csv, pero creo que la búsqueda fue extraña. Creo que se equivocó por eso. Sin embargo no te preocupes. Siempre estoy recopilando copias de seguridad y este ni siquiera era el conjunto de datos real, solo uno de 1 gb. El real es un 26gb.
Ludovic Zenohate Lagouardette
14

Si no tiene acceso a dos2unix , pero puede leer esta página, puede copiar / pegar dos2unix.py desde aquí.

#!/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))

Publicación cruzada del superusuario .

anatoly techtonik
fuente
1
El uso es engañoso. El real dos2unixconvierte todos los archivos de entrada por defecto. Su uso implica -nparámetro. Y lo real dos2unixes un filtro que lee desde stdin, escribe en stdout si no se proporcionan los archivos.
jfs
8

Super duper fácil con PCRE;

Como secuencia de comandos, o reemplazar $@con sus archivos.

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

¡Esto sobrescribirá sus archivos en su lugar!

Recomiendo hacer esto solo con una copia de seguridad (control de versiones o de otra manera)

ThorSummoner
fuente
¡Gracias! Esto funciona, aunque estoy escribiendo el nombre del archivo y no --. Elegí esta solución porque es fácil de entender y adaptar para mí. Para su información, esto es lo que hacen los interruptores: -pasumir un bucle "mientras que la entrada", -ieditar el archivo de entrada en su lugar, -eejecutar el siguiente comando
Rolf
Estrictamente hablando, PCRE es una reimplementación del motor de expresiones regulares de Perl, no el motor de expresiones regulares de Perl. Ambos tienen esta capacidad, aunque también hay diferencias, a pesar de la implicación en el nombre.
tripleee
6

Una solución awk aún más simple sin un programa:

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

Técnicamente '1' es su programa, b / c awk requiere uno cuando se le da la opción.

ACTUALIZACIÓN : Después de volver a visitar esta página por primera vez en mucho tiempo, me di cuenta de que nadie había publicado aún una solución interna, así que aquí hay una:

while IFS= read -r line;
do printf '%s\n' "${line%$'\r'}";
done < dos.txt > unix.txt
nawK
fuente
Eso es útil, pero para ser claros: esto traduce Unix -> Windows / DOS, que es la dirección opuesta a lo que solicitó el OP.
mklement0
55
Fue hecho a propósito, dejado como ejercicio para el autor. eyerolls awk -v RS='\r\n' '1' dos.txt > unix.txt
nawK
Genial (y felicitaciones por la delicadeza pedagógica).
mklement0
1
"b / c awk requiere uno cuando se le da la opción". - awk siempre requiere un programa, ya sea que las opciones estén especificadas o no.
mklement0
1
La solución bash pura es interesante, pero mucho más lenta que una solución awko equivalente sed. Además, debe usar while IFS= read -r linepara preservar fielmente las líneas de entrada, de lo contrario se recortará el espacio en blanco inicial y final (alternativamente, no use ningún nombre de variable en el readcomando y trabaje con $REPLY).
mklement0
5

Solo tenía que reflexionar sobre la misma pregunta (en el lado de Windows, pero igualmente aplicable a Linux). Sorprendentemente, nadie mencionó una forma muy automatizada de hacer la conversión CRLF <-> LF para archivos de texto usando la buena zip -llopción anterior (Info-ZIP):

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

NOTA: esto crearía un archivo zip conservando los nombres de archivo originales pero convirtiendo las terminaciones de línea a LF. Luego unzipextraería los archivos como zip'ed, es decir, con sus nombres originales (pero con terminaciones LF), lo que provocaría sobrescribir los archivos originales locales, si los hubiera.

Extracto relevante de zip --help:

zip --help
...
-l   convert LF to CR LF (-ll CR LF to LF)
vmsnomad
fuente
La mejor respuesta, según yo, ya que puede procesar directorios y subdirectorios completos. Me alegro de haber cavado tan lejos.
caram
5

Curiosamente en mi git-bash en windows ya sed ""hizo el truco:

$ 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

Supongo que sed los ignora al leer líneas de entrada y siempre escribe terminaciones de línea unix en la salida.

usuario829755
fuente
4

Esto funciono para mi

tr "\r" "\n" < sampledata.csv > sampledata2.csv 
Santosh
fuente
99
Esto convertirá cada sola DOS-salto de línea en dos UNIX-saltos de línea.
Melebius
2

Para Mac osx si tiene instalado homebrew [ http://brew.sh/font>[1]

brew install dos2unix

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

Asegúrese de haber realizado copias de los archivos, ya que este comando modificará los archivos en su lugar. La opción -c mac hace que el conmutador sea compatible con osx.

Ashley Raiteri
fuente
Esta respuesta realmente no es la pregunta del póster original.
hlin117
2
Los usuarios de OS X no deben usar -c mac, que es para convertir solo CRnuevas líneas anteriores a OS X. Desea usar ese modo solo para archivos hacia y desde Mac OS 9 o anterior.
askewchan
2

TIMTOWTDI!

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

Basado en @GordonDavisson

Hay que considerar la posibilidad de [noeol]...

lzc
fuente
2

Puedes usar awk. Establezca el separador de registros ( RS) en una expresión regular que coincida con todos los caracteres o caracteres de nueva línea posibles. Y establezca el separador de registro de salida ( ORS) en el carácter de nueva línea de estilo Unix.

awk 'BEGIN{RS="\r|\n|\r\n|\n\r";ORS="\n"}{print}' windows_or_macos.txt > unix.txt
kazmer
fuente
Ese fue el que funcionó para mí (MacOS, git diffmuestra ^ M, editado en vim)
Dorian
2

En Linux es fácil convertir ^ M (ctrl-M) a * nix newlines (^ J) con sed.

Será algo así en la CLI, en realidad habrá un salto de línea en el texto. Sin embargo, el \ pasa ese ^ J a sed:

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

Obtiene esto usando ^ V (ctrl-V), ^ M (ctrl-M) y \ (barra invertida) mientras escribe:

sed 's/^V^M/\^V^J/g' < ffmpeg.log > new.log
jet
fuente
2
sed --expression='s/\r\n/\n/g'

Como la pregunta menciona sed, esta es la forma más directa de usar sed para lograr esto. Lo que dice la expresión es reemplazar todo retorno de carro y avance de línea solo con avance de línea. Eso es lo que necesitas cuando pasas de Windows a Unix. Verifiqué que funciona.

Juan Pablo
fuente
Hola, John Paul: esta respuesta se marcó para su eliminación, así que apareció en una cola de revisión para mí. En general, cuando tiene una pregunta como esta que tiene 8 años, con 22 respuestas, querrá explicar cómo su respuesta es útil de una manera que otras respuestas existentes no lo son.
zzxyz
0

Como una extensión de la solución Unix a DOS de Jonathan Leffler, para convertir de manera segura a DOS cuando no esté seguro de las terminaciones de línea actuales del archivo:

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

Esto verifica que la línea no termine en CRLF antes de convertir a CRLF.

Gannet
fuente
0

Hice un script basado en la respuesta aceptada para que pueda convertirlo directamente sin necesidad de un archivo adicional al final y eliminar y cambiar el nombre después.

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

solo asegúrese de que si tiene un archivo como "file1.txt" que "file1.txt2" ya no existe o se sobrescribirá, lo uso como un lugar temporal para almacenar el archivo.

OZZIE
fuente
0

Con bash 4.2 y versiones más recientes, puede usar algo como esto para eliminar el CR final, que solo usa las funciones integradas de bash:

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