Come convertire DOS / Windows Newline (CRLF) in Unix Newline (LF) in uno script Bash?


336

Come posso viconvertire programmaticamente (cioè, non usare ) le nuove righe DOS / Windows in Unix?

I comandi dos2unixe unix2dosnon sono disponibili su alcuni sistemi. Come posso emularli con comandi come sed/ awk/ tr?


9
In generale, basta installarlo dos2unixusando il gestore pacchetti, è davvero molto più semplice ed esiste sulla maggior parte delle piattaforme.
Brad Koch,

1
Concordato! @BradKoch Semplice come 'brew install dos2unix' su Mac OSX
SmileIT

Risposte:


323

Puoi usare trper convertire da DOS in Unix; tuttavia, puoi farlo in modo sicuro solo se CR appare nel tuo file solo come primo byte di una coppia di byte CRLF. Questo di solito è il caso. Quindi utilizzare:

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

Si noti che il nome DOS-fileè diverso dal nome UNIX-file; se provi a usare lo stesso nome due volte, finirai con nessun dato nel file.

Non puoi farlo al contrario (con lo standard 'tr').

Se sai come inserire il ritorno a capo in uno script ( control-V, control-Mper inserire control-M), allora:

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

dove '^ M' è il carattere control-M. È inoltre possibile utilizzare il meccanismo di bash quotazione ANSI-C per specificare il ritorno a capo:

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

Tuttavia, se devi farlo molto spesso (più di una volta, in parole povere), è molto più sensato installare i programmi di conversione (ad es. dos2unixE unix2dos, o forse dtoue utod) e usarli.

Se è necessario elaborare intere directory e sottodirectory, è possibile utilizzare zip:

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

Ciò creerà un archivio zip con terminazioni di riga modificate da CRLF a CR. unziprimetterà quindi a posto i file convertiti (e ti chiederà file per file: puoi rispondere: Sì a tutti). Ringraziamenti a @vmsnomad per averlo segnalato.


9
usando tr -d '\015' <DOS-file >UNIX-filewhere DOS-file== UNIX-filerisulta solo un file vuoto. Il file di output deve essere un file diverso, sfortunatamente.
Buttle Butkus,

3
@ButtleButkus: Beh, sì; ecco perché ho usato due nomi diversi. Se si esegue lo zapping del file di input prima che il programma legga tutto, come si fa quando si utilizza lo stesso nome due volte, si finisce con un file vuoto. Questo è un comportamento uniforme su sistemi simili a Unix. Richiede un codice speciale per gestire in modo sicuro la sovrascrittura di un file di input. Segui le istruzioni e sarai OK.
Jonathan Leffler,

Mi sembra di ricordare da qualche parte la funzionalità di sostituzione della ricerca nel file.
Buttle Butkus,

4
Ci sono posti; devi sapere dove trovarli. Entro certi limiti, l' sedopzione GNU -i(per sul posto) funziona; i limiti sono file collegati e collegamenti simbolici. Il sortcomando ha 'sempre' (dal 1979, se non in precedenza) supportato l' -oopzione che può elencare uno dei file di input. Tuttavia, ciò è in parte dovuto al fatto che sortdeve leggere tutto il suo input prima di poter scrivere qualsiasi suo output. Altri programmi supportano sporadicamente la sovrascrittura di uno dei loro file di input. È possibile trovare un programma generico (script) per evitare problemi in "L'ambiente di programmazione UNIX" di Kernighan & Pike.
Jonathan Leffler,

3
La terza opzione ha funzionato per me, grazie. Ho usato l'opzione -i: sed -i $'s/\r$//' filename- per modificare sul posto. Sto lavorando su una macchina che non ha accesso a Internet, quindi l'installazione del software è un problema.
Warren Dew,

64
tr -d "\r" < file

dai un'occhiata qui per esempi 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

Utilizzare sed -iper la conversione sul posto, ad es sed -i 's/..../' file.


10
Ho usato una variante poiché il mio file aveva solo \r:tr "\r" "\n" < infile > outfile
Matt Todd,

1
@MattTodd potresti postarlo come risposta? l' -dè descritto più frequentemente e non aiuterà nella "sola \r" situazione.
n611x007,

5
Si noti che la proposta \rdi \nmappatura ha l'effetto di spaziare due volte i file; ogni singola riga CRLF che termina in DOS diventa \n\nin Unix.
Jonathan Leffler,

Posso farlo in modo ricorsivo?
Aaron Franke il

36

Farlo con POSIX è complicato:

  • POSIX Sed non supporta \ro \15. Anche se così fosse, l'opzione sul posto -inon è POSIX

  • POSIX Awk supporta \re \15, tuttavia, l' -i inplaceopzione non è POSIX

  • D2U e dos2unix non sono POSIX utilità , ma ex è

  • POSIX ex non supporta \r, \15, \no\12

Per rimuovere i ritorni a capo:

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

Per aggiungere resi di trasporto:

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

2
Sembra che supporti POSIX . tr\r Quindi potresti anche usare printf '%s\n' '%!tr -d "\r"' x | ex file(anche se concesso, rimosso \ranche se non immediatamente precedente \n). Inoltre, l' -bopzione per exnon è specificata da POSIX.
Carattere jolly

1
Fare questo in POSIX è facile. Incorpora il letterale CR nello script digitandolo (è control-M).
Giosuè,

28

Puoi usare vim a livello di codice con l'opzione -c {comando}:

Dos to Unix:

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

Unix da dosare:

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

"set ff = unix / dos" significa cambiare il formato file (ff) del file nel formato di fine riga Unix / DOS

": wq" significa scrivere il file su disco ed uscire dall'editor (permettendo di usare il comando in un ciclo)


3
Sembrava la soluzione più elegante, ma la mancanza di spiegazioni sul significato di wq è sfortunata.
Jorrick Sleijster,

5
Chiunque usi visaprà cosa :wqsignifica. Per quelli che non significano 3 caratteri 1) apri l'area di comando vi, 2) scrivi e 3) esci.
David Newcomb,

Non avevo idea che potessi aggiungere interattivamente comandi a Vim dalla CLI
Robert Dundon,

puoi usare ": x" invece di ": wq"
JosephConrad il

25

Usando AWK puoi fare:

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

Usando Perl puoi fare:

perl -pe 's/\r$//' < dos.txt > unix.txt

2
Una soluzione piacevole e portatile awk .
mklement0,

24

Per convertire un file sul posto, utilizzare

dos2unix <filename>

Per generare il testo convertito in un altro file, utilizzare

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

Puoi installarlo su Ubuntu o Debian con

sudo apt install dos2unix

o su macOS usando homebrew

brew install dos2unix

1
So che la domanda richiede alternative a dos2unix ma è il primo risultato di Google.
Boris,

18

Questo problema può essere risolto con strumenti standard, ma ci sono abbastanza trappole per gli inconsapevoli che ti consiglio di installare il flipcomando, che è stato scritto più di 20 anni fa da Rahul Dhesi, l'autore di zoo. Fa un ottimo lavoro convertendo i formati di file mentre, ad esempio, evita la distruzione involontaria di file binari, il che è un po 'troppo facile se corri solo alterando ogni CRLF che vedi ...


Un modo per farlo in modo streaming, senza modificare il file originale?
augurar

@augurar è possibile controllare "pacchetti simili" pacchetti.debian.org/wheezy/flip
n611x007

Ho avuto l'esperienza di rompere metà del mio sistema operativo semplicemente eseguendo texxto con una bandiera sbagliata. Fai attenzione soprattutto se vuoi farlo su intere cartelle.
A_P,

14

Le soluzioni pubblicate finora affrontano solo una parte del problema, convertendo il CRLF di DOS / Windows in LF di Unix; la parte che manca è che DOS usa CRLF come separatore di linea , mentre Unix usa LF come terminatore di linea . La differenza è che un file DOS (di solito) non avrà nulla dopo l'ultima riga del file, mentre Unix lo farà. Per eseguire correttamente la conversione, è necessario aggiungere quell'LF finale (a meno che il file non abbia lunghezza zero, cioè non contenga alcuna riga). Il mio incantesimo preferito per questo (con una piccola logica aggiunta per gestire i file separati da CR in stile Mac e non i file molest che sono già in formato unix) è un po 'di perl:

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

Si noti che questo invia la versione Unixified del file a stdout. Se vuoi sostituire il file con una versione Unixified, aggiungi il -iflag di perl .


@LudovicZenohateLagouardette Era un semplice file di testo (ad esempio csv o testo con tabulazione) o qualcos'altro? Se fosse in un formato di database, manipolarlo come se fosse un testo è molto probabile che danneggi la sua struttura interna.
Gordon Davisson,

Un semplice testo CSV, ma penso che l'incondizionamento fosse strano. Penso che sia incasinato a causa di quello. Tuttavia non preoccuparti. Colleziono sempre backup e questo non era nemmeno il vero set di dati, solo 1 GB. Il vero è un 26 GB.
Ludovic Zenohate Lagouardette,

14

Se non hai accesso a dos2unix , ma puoi leggere questa pagina, puoi copiare / incollare dos2unix.py da qui.

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

Trasmissione incrociata da superutente .


1
L'uso è fuorviante. Il reale dos2unixconverte tutti i file di input per impostazione predefinita. Il tuo utilizzo implica un -nparametro. E il reale dos2unixè un filtro che legge da stdin, scrive su stdout se i file non vengono dati.
jfs,

8

Super duper facile con PCRE;

Come script o sostituisci $@con i tuoi file.

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

Questo sovrascriverà i tuoi file sul posto!

Consiglio di farlo solo con un backup (controllo versione o altro)


Grazie! Funziona, anche se sto scrivendo il nome del file e no --. Ho scelto questa soluzione perché è facile da capire e adattarmi per me. Cordiali saluti, questo è ciò che fanno gli switch: -ppresuppone un ciclo "while input", -imodifica il file di input sul posto, -eesegui il seguente comando
Rolf

A rigor di termini, PCRE è una reimplementazione del motore regex di Perl, non del motore regex di Perl. Entrambi hanno questa capacità, anche se ci sono anche differenze, nonostante l'implicazione nel nome.
Tripleee,

6

Una soluzione awk ancora più semplice senza programma:

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

Tecnicamente '1' è il tuo programma, b / c awk ne richiede uno quando viene data l'opzione.

AGGIORNAMENTO : Dopo aver rivisitato questa pagina per la prima volta da molto tempo, mi sono reso conto che nessuno ha ancora pubblicato una soluzione interna, quindi eccone una:

while IFS= read -r line;
do printf '%s\n' "${line%$'\r'}";
done < dos.txt > unix.txt

È utile, ma solo per essere chiari: questo traduce Unix -> Windows / DOS, che è la direzione opposta a quella richiesta dall'OP.
mklement0,

5
È stato fatto apposta, lasciato come esercizio per l'autore. eyerolls awk -v RS='\r\n' '1' dos.txt > unix.txt
nawK

Fantastico (e complimenti a te per la finezza pedagogica).
mklement0

1
"b / c awk richiede uno quando viene data l'opzione." - awk richiede sempre un programma, indipendentemente dal fatto che le opzioni siano specificate o meno.
mklement0

1
La soluzione bash pura è interessante, ma molto più lento di un equivalente awko di sedsoluzione. Inoltre, è necessario utilizzare while IFS= read -r lineper preservare fedelmente le righe di input, altrimenti viene tagliato lo spazio bianco iniziale e finale (in alternativa, non utilizzare alcun nome di variabile nel readcomando e lavorare con $REPLY).
mklement0

5

Dovevo solo ponderare la stessa domanda (sul lato Windows, ma ugualmente applicabile a Linux). Sorprendentemente nessuno ha menzionato un modo molto automatizzato di fare la conversione CFF <-> LF per file di testo usando una buona vecchia zip -llopzione (Info-ZIP):

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

NOTA: questo creerebbe un file zip preservando i nomi dei file originali ma convertendo le terminazioni di linea in LF. Poiunzip estrarrebbe i file come compressi, cioè con i loro nomi originali (ma con terminazioni LF), spingendo così a sovrascrivere i file originali locali se presenti.

Estratto rilevante dal zip --help:

zip --help
...
-l   convert LF to CR LF (-ll CR LF to LF)

La migliore risposta, secondo me, in quanto può elaborare intere directory e sottodirectory. Sono contento di aver scavato così in basso.
Caram

5

è interessante notare che nel mio git-bash su Windows sed ""ho già fatto il trucco:

$ 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

La mia ipotesi è che sed li ignori quando legge le righe dall'input e scrive sempre le terminazioni delle linee unix sull'output.


4

Questo ha funzionato per me

tr "\r" "\n" < sampledata.csv > sampledata2.csv 

9
In questo modo ogni singola riga di DOS verrà convertita in due righe di UNIX.
Melebio

2

Per Mac OSX se hai installato homebrew [ http://brew.sh/[[1]

brew install dos2unix

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

Assicurati di aver fatto copie dei file, poiché questo comando modificherà i file in atto. L'opzione -c mac rende lo switch compatibile con osx.


Questa risposta non fa davvero la domanda del poster originale.
hlin117,

2
Gli utenti di OS X non dovrebbero usare -c mac, che è per convertire CRsolo le newline pre-OS X. Si desidera utilizzare quella modalità solo per i file da e verso Mac OS 9 o precedenti.
askewchan,

2

TIMTOWTDI!

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

Basato su @GordonDavisson

Bisogna considerare la possibilità di [noeol]...


2

Puoi usare awk. Impostare il separatore record ( RS) su una regexp che corrisponda a tutti i possibili caratteri di nuova riga o caratteri. E imposta il separatore del record di output ( ORS) sul carattere newline in stile unix.

awk 'BEGIN{RS="\r|\n|\r\n|\n\r";ORS="\n"}{print}' windows_or_macos.txt > unix.txt

Questo è quello che ha funzionato per me (MacOS, git diffmostra ^ M, modificato in vim)
Dorian

2

Su Linux è facile convertire ^ M (ctrl-M) in * nix newline (^ J) con sed.

Sarà qualcosa di simile sulla CLI, ci sarà effettivamente un'interruzione di riga nel testo. Tuttavia, il \ passa quel ^ J insieme a sed:

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

Puoi ottenere ciò usando ^ V (ctrl-V), ^ M (ctrl-M) e \ (barra rovesciata) mentre digiti:

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

2
sed --expression='s/\r\n/\n/g'

Poiché la domanda menziona sed, questo è il modo più semplice per usare sed per raggiungere questo obiettivo. Quello che dice l'espressione è sostituire tutto il ritorno a capo e l'avanzamento riga con solo avanzamento riga. Questo è ciò di cui hai bisogno quando passi da Windows a Unix. Ho verificato che funziona.


Ehi, John Paul: questa risposta è stata contrassegnata per l'eliminazione, quindi sono arrivato in una coda di recensioni per me. In generale, quando hai una domanda come questa che ha 8 anni, con 22 risposte, ti consigliamo di spiegare come la tua risposta è utile in un modo in cui non lo sono altre risposte esistenti.
zzxyz,

0

Come estensione della soluzione Unix in DOS di Jonathan Leffler, per convertire in modo sicuro in DOS quando non si è sicuri delle terminazioni di riga correnti del file:

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

Ciò verifica che la riga non finisca già in CRLF prima della conversione in CRLF.


0

Ho creato uno script basato sulla risposta accettata in modo da poterlo convertire direttamente senza bisogno di un file aggiuntivo alla fine e rimuovere e rinominare in seguito.

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

assicurati solo che se hai un file come "file1.txt" che "file1.txt2" non esiste già o verrà sovrascritto, lo uso come luogo temporaneo in cui archiviare il file.


0

Con bash 4.2 e versioni successive è possibile utilizzare qualcosa del genere per eliminare il CR finale, che utilizza solo gli incorporamenti bash:

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.