Wie konvertiere ich DOS / Windows Newline (CRLF) in Unix Newline (LF) in einem Bash-Skript?

336

Wie kann ich viDOS / Windows-Zeilenumbrüche programmgesteuert (dh nicht verwenden ) in Unix konvertieren?

Die Befehle dos2unixund unix2dossind auf bestimmten Systemen nicht verfügbar. Wie kann ich diese mit Befehlen wie sed/ awk/ emulieren tr?

Koran Molovik
quelle
9
Im Allgemeinen installieren dos2unixSie es einfach mit Ihrem Paketmanager. Es ist viel einfacher und auf den meisten Plattformen vorhanden.
Brad Koch
1
Einverstanden! @BradKoch Einfach wie 'Brew Install Dos2unix' unter Mac OSX
SmileIT

Antworten:

322

Sie können verwenden tr, um von DOS nach Unix zu konvertieren; Sie können dies jedoch nur sicher tun, wenn CR in Ihrer Datei nur als erstes Byte eines CRLF-Bytepaars angezeigt wird. Dies ist normalerweise der Fall. Sie verwenden dann:

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

Beachten Sie, dass sich der Name DOS-filevom Namen unterscheidet UNIX-file. Wenn Sie versuchen, denselben Namen zweimal zu verwenden, werden keine Daten in der Datei angezeigt.

Sie können es nicht umgekehrt machen (mit Standard 'tr').

Wenn Sie wissen , wie Wagenrücklauf in ein Skript ein ( control-V, control-MSteuer-M eingeben), dann gilt :

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

wobei das '^ M' das Steuer-M-Zeichen ist. Sie können auch den bash ANSI-C-Angebotsmechanismus verwenden, um den Wagenrücklauf anzugeben:

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

Wenn Sie dies jedoch sehr oft tun müssen (grob gesagt mehr als einmal), ist es weitaus sinnvoller, die Konvertierungsprogramme (z. B. dos2unixund unix2dosoder oder dtouund utod) zu installieren und zu verwenden.

Wenn Sie ganze Verzeichnisse und Unterverzeichnisse verarbeiten müssen, können Sie Folgendes verwenden zip:

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

Dadurch wird ein Zip-Archiv erstellt, dessen Zeilenenden von CRLF in CR geändert werden. unzipsetzt dann die konvertierten Dateien wieder ein (und fragt Sie Datei für Datei - Sie können antworten: Ja zu allen). Dank an @vmsnomad für den Hinweis.

Jonathan Leffler
quelle
9
Die Verwendung von tr -d '\015' <DOS-file >UNIX-filewhere DOS-file== führt UNIX-filenur zu einer leeren Datei. Die Ausgabedatei muss leider eine andere Datei sein.
Buttle Butkus
3
@ ButtleButkus: Nun ja; Deshalb habe ich zwei verschiedene Namen verwendet. Wenn Sie die Eingabedatei zappen, bevor das Programm alles liest, wie Sie es tun, wenn Sie denselben Namen zweimal verwenden, erhalten Sie eine leere Datei. Das ist ein einheitliches Verhalten auf Unix-ähnlichen Systemen. Es erfordert speziellen Code, um das Überschreiben einer Eingabedatei sicher zu handhaben. Befolgen Sie die Anweisungen und Sie werden in Ordnung sein.
Jonathan Leffler
Ich erinnere mich anscheinend an die Funktion zum Suchen und Ersetzen in der Datei.
Buttle Butkus
4
Es gibt Orte; Sie müssen wissen, wo sie zu finden sind. Innerhalb bestimmter Grenzen funktioniert die GNU- sedOption -i(für In-Place). Die Grenzwerte sind verknüpfte Dateien und Symlinks. Der sortBefehl hat 'immer' (seit 1979, wenn nicht früher) die -oOption unterstützt, mit der eine der Eingabedateien aufgelistet werden kann. Dies liegt jedoch teilweise daran, dass sortalle Eingaben gelesen werden müssen, bevor eine Ausgabe geschrieben werden kann. Andere Programme unterstützen sporadisch das Überschreiben einer ihrer Eingabedateien. In 'The UNIX Programming Environment' von Kernighan & Pike finden Sie ein Allzweckprogramm (Skript), um Probleme zu vermeiden .
Jonathan Leffler
3
Die dritte Option hat bei mir funktioniert, danke. Ich habe die Option -i verwendet: sed -i $'s/\r$//' filename-, um an Ort und Stelle zu bearbeiten. Ich arbeite an einem Computer, der keinen Zugang zum Internet hat, daher ist die Installation der Software ein Problem.
Warren Dew
64
tr -d "\r" < file

Schauen Sie sich hier Beispiele an, die Folgendes verwenden 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

Verwendung sed -ifür In-Place-Konvertierung, z sed -i 's/..../' file.

Ghostdog74
quelle
10
Ich habe eine Variante verwendet, da meine Datei nur hatte \r:tr "\r" "\n" < infile > outfile
Matt Todd
1
@MattTodd könntest du dies als Antwort posten? Das -dwird häufiger vorgestellt und hilft in der "einzigen \r" Situation nicht weiter.
n611x007
5
Beachten Sie, dass die vorgeschlagene \rzum \nMapping hat die Wirkung von doppelsträngigen Beabstanden der Dateien; Jede einzelne CRLF-Zeile, die unter DOS endet, wird \n\nunter Unix.
Jonathan Leffler
Kann ich das rekursiv machen?
Aaron Franke
36

Dies mit POSIX zu tun ist schwierig:

  • POSIX Sed unterstützt \roder nicht \15. Selbst wenn dies der Fall -iist , ist die Option an Ort und Stelle nicht POSIX

  • POSIX Awk unterstützt \rund \15die -i inplaceOption ist jedoch nicht POSIX

  • d2u und dos2unix sind keine POSIX-Dienstprogramme , aber ex ist

  • POSIX ex nicht unterstützt \r, \15, \noder\12

So entfernen Sie Wagenrückläufe:

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

So fügen Sie Wagenrückläufe hinzu:

ex -bsc '%!awk "{sub(/$/,\"\r\")}1"' -cx file
Steven Penny
quelle
2
Es sieht so aus, als ob POSIX trunterstützt \r. Sie können also auch verwenden printf '%s\n' '%!tr -d "\r"' x | ex file(obwohl dies gewährt wurde, wurde dies entfernt, \rauch wenn es nicht unmittelbar vorhergeht \n). Außerdem wird die -bOption zu exnicht von POSIX angegeben.
Wildcard
1
Dies in POSIX zu tun ist einfach. Betten Sie das CR-Literal in das Skript ein, indem Sie es eingeben (es ist Control-M).
Joshua
28

Sie können vim programmgesteuert mit der Option -c {Befehl} verwenden:

Dos to Unix:

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

Unix zu dos:

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

"set ff = unix / dos" bedeutet, dass das Dateiformat (ff) der Datei in das Unix / DOS-Zeilenendeformat geändert wird

": wq" bedeutet, dass eine Datei auf die Festplatte geschrieben und der Editor beendet wird (wobei der Befehl in einer Schleife verwendet werden kann).

Johan Zicola
quelle
3
Dies schien die eleganteste Lösung zu sein, aber das Fehlen einer Erklärung darüber, was wq bedeutet, ist unglücklich.
Jorrick Sleijster
4
Jeder, der verwendet, viwird wissen, was :wqbedeutet. Für diejenigen, die dies nicht tun, bedeuten die 3 Zeichen 1) vi-Befehlsbereich öffnen, 2) schreiben und 3) beenden.
David Newcomb
Ich hatte keine Ahnung, dass Sie vim interaktiv Befehle von der CLI hinzufügen könnten
Robert Dundon
Sie können ": x" anstelle von ": wq" verwenden
JosephConrad
25

Mit AWK können Sie Folgendes tun:

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

Mit Perl können Sie Folgendes tun:

perl -pe 's/\r$//' < dos.txt > unix.txt
Codaddict
quelle
2
Eine schöne, tragbare awk Lösung.
mklement0
23

Verwenden Sie zum Konvertieren einer vorhandenen Datei

dos2unix <filename>

Um konvertierten Text in eine andere Datei auszugeben, verwenden Sie

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

Sie können es unter Ubuntu oder Debian mit installieren

sudo apt install dos2unix

oder unter macOS mit homebrew

brew install dos2unix
Boris
quelle
1
Ich weiß, dass die Frage nach Alternativen zu dos2unix fragt, aber es ist das erste Google-Ergebnis.
Boris
18

Dieses Problem kann mit Standardwerkzeugen gelöst werden, aber es gibt genügend Fallen für Unvorsichtige, die ich empfehle, den flipBefehl zu installieren , der vor über 20 Jahren von Rahul Dhesi, dem Autor von, geschrieben wurde zoo. Es konvertiert hervorragend Dateiformate und vermeidet beispielsweise die versehentliche Zerstörung von Binärdateien. Dies ist etwas zu einfach, wenn Sie nur herumrennen und jede CRLF ändern, die Sie sehen ...

Norman Ramsey
quelle
Gibt es eine Möglichkeit, dies im Streaming zu tun, ohne die Originaldatei zu ändern?
Augurar
@augurar können Sie "ähnliche Pakete" überprüfen packages.debian.org/wheezy/flip
n611x007
Ich hatte die Erfahrung, die Hälfte meines Betriebssystems zu beschädigen, indem ich texxto mit einem falschen Flag ausführte. Seien Sie besonders vorsichtig, wenn Sie dies für ganze Ordner tun möchten.
A_P
14

Die bisher veröffentlichten Lösungen befassen sich nur mit einem Teil des Problems und konvertieren die CRLF von DOS / Windows in die LF von Unix. der Teil sind vermisst sie ist , dass DOS Verwendung CRLF als Zeilentrennzeichen , während Unix LF als Linie verwendet Terminator . Der Unterschied besteht darin, dass eine DOS-Datei (normalerweise) nach der letzten Zeile in der Datei nichts mehr enthält, während dies unter Unix der Fall ist. Um die Konvertierung ordnungsgemäß durchzuführen, müssen Sie diesen endgültigen LF hinzufügen (es sei denn, die Datei hat die Länge Null, dh sie enthält überhaupt keine Zeilen). Meine Lieblingsbeschwörung dafür (mit ein wenig zusätzlicher Logik für den Umgang mit CR-getrennten Dateien im Mac-Stil und nicht für belästigende Dateien, die bereits im Unix-Format vorliegen) ist ein bisschen Perl:

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

Beachten Sie, dass dadurch die Unixified-Version der Datei an stdout gesendet wird. Wenn Sie die Datei durch eine Unixified-Version ersetzen möchten, fügen Sie das Perl- -iFlag hinzu.

Gordon Davisson
quelle
@LudovicZenohateLagouardette War es eine reine Textdatei (dh CSV- oder tab-demitierter Text) oder etwas anderes? Wenn es in einem datenbankähnlichen Format vorliegt, kann es sehr wahrscheinlich zu einer Beschädigung der internen Struktur kommen, wenn es so manipuliert wird, als wäre es Text.
Gordon Davisson
Ein CSV im Klartext, aber ich finde die Folge seltsam. Ich denke, es hat das durcheinander gebracht. Aber mach dir keine Sorgen. Ich sammle immer Backups und dies war nicht einmal der eigentliche Datensatz, sondern nur ein 1-GB-Datensatz. Der echte ist ein 26GB.
Ludovic Zenohate Lagouardette
14

Wenn Sie keinen Zugriff auf dos2unix haben , diese Seite jedoch lesen können, können Sie dos2unix.py von hier aus kopieren / einfügen .

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

Cross-posted vom Superuser .

anatoly techtonik
quelle
1
Die Verwendung ist irreführend. Der Real dos2unixkonvertiert standardmäßig alle Eingabedateien. Ihre Verwendung impliziert -nParameter. Und der Real dos2unixist ein Filter, der aus stdin liest und in stdout schreibt, wenn die Dateien nicht angegeben werden.
JFS
8

Super super einfach mit PCRE;

Als Skript oder durch $@Ihre Dateien ersetzen .

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

Dadurch werden Ihre Dateien an Ort und Stelle überschrieben!

Ich empfehle dies nur mit einem Backup (Versionskontrolle oder auf andere Weise).

ThorSummoner
quelle
Vielen Dank! Dies funktioniert, obwohl ich den Dateinamen schreibe und nein --. Ich habe mich für diese Lösung entschieden, weil sie für mich leicht zu verstehen und anzupassen ist. Zu Ihrer Information, dies ist, was die Schalter tun: -pNehmen Sie eine "while input" -Schleife an, -ibearbeiten Sie die Eingabedatei an Ort und Stelle, -eführen Sie den folgenden Befehl aus
Rolf
Genau genommen ist PCRE eine Neuimplementierung der Regex-Engine von Perl, nicht der Regex-Engine von Perl. Sie haben beide diese Fähigkeit, obwohl es auch Unterschiede gibt, trotz der Implikation im Namen.
Tripleee
6

Eine noch einfachere awk-Lösung ohne Programm:

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

Technisch gesehen ist '1' Ihr Programm, für b / c awk ist eines erforderlich, wenn die Option angegeben ist.

UPDATE : Nachdem ich diese Seite zum ersten Mal seit langer Zeit wieder besucht hatte, stellte ich fest, dass noch niemand eine interne Lösung veröffentlicht hat. Hier ist eine:

while IFS= read -r line;
do printf '%s\n' "${line%$'\r'}";
done < dos.txt > unix.txt
nawK
quelle
Das ist praktisch, aber um es klar zu machen: Dies übersetzt Unix -> Windows / DOS, was die entgegengesetzte Richtung ist, nach der das OP gefragt hat.
mklement0
5
Es wurde absichtlich gemacht und als Übung für den Autor hinterlassen. Eyerolls awk -v RS='\r\n' '1' dos.txt > unix.txt
NawK
Großartig (und ein großes Lob an Sie für pädagogische Finesse).
mklement0
1
"b / c awk erfordert eine, wenn eine Option angegeben ist." - awk benötigt immer ein Programm, unabhängig davon, ob Optionen angegeben sind oder nicht.
mklement0
1
Die reine Bash-Lösung ist interessant, aber viel langsamer als ein Äquivalent awkoder eine sedLösung. Außerdem müssen Sie verwenden while IFS= read -r line, um die Eingabezeilen originalgetreu beizubehalten, da sonst führende und nachfolgende Leerzeichen abgeschnitten werden (alternativ können Sie im readBefehl keinen Variablennamen verwenden und damit arbeiten $REPLY).
mklement0
5

Interessanterweise hat mein Git-Bash auf Windows sed ""schon den Trick gemacht:

$ 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

Ich vermute, dass sed sie beim Lesen von Zeilen aus der Eingabe ignoriert und immer Unix-Zeilenenden in die Ausgabe schreibt.

user829755
quelle
4

Das hat bei mir funktioniert

tr "\r" "\n" < sampledata.csv > sampledata2.csv 
Santosh
quelle
9
Dadurch wird jede einzelne DOS-Newline in zwei UNIX-Newlines konvertiert.
Melebius
4

Ich musste nur über dieselbe Frage nachdenken (auf Windows-Seite, aber gleichermaßen für Linux). Überraschenderweise erwähnte niemand eine sehr automatisierte Methode zur CRLF <-> LF-Konvertierung für Textdateien mit der guten alten zip -llOption (Info-ZIP):

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

HINWEIS: Dadurch wird eine Zip-Datei erstellt, die die ursprünglichen Dateinamen beibehält, aber die Zeilenenden in LF konvertiert. Dann unzipwürden die Dateien als zip'ed extrahiert, dh mit ihren ursprünglichen Namen (aber mit LF-Endungen), wodurch die lokalen Originaldateien, falls vorhanden, überschrieben werden.

Relevanter Auszug aus zip --help:

zip --help
...
-l   convert LF to CR LF (-ll CR LF to LF)
vmsnomad
quelle
Meiner Meinung nach die beste Antwort, da es ganze Verzeichnisse und Unterverzeichnisse verarbeiten kann. Ich bin froh, dass ich so weit unten gegraben habe.
Karam
2

Für Mac OSX, wenn Sie Homebrew installiert haben [ http://brew.sh/ weibliches [1 ]

brew install dos2unix

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

Stellen Sie sicher, dass Sie Kopien der Dateien erstellt haben, da dieser Befehl die vorhandenen Dateien ändert. Mit der Option -c mac ist der Switch mit osx kompatibel.

Ashley Raiteri
quelle
Diese Antwort entspricht wirklich nicht der Frage des Originalplakats.
hlin117
2
OS X-Benutzer sollten nicht verwenden -c mac, da dies nur zum Konvertieren von CRZeilenumbrüchen vor OS X dient. Sie möchten diesen Modus nur für Dateien von und nach Mac OS 9 oder früher verwenden.
Askewchan
2

TIMTOWTDI!

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

Basierend auf @GordonDavisson

Man muss die Möglichkeit in Betracht ziehen [noeol]...

lzc
quelle
2

Sie können awk verwenden. Setzen Sie das Datensatztrennzeichen ( RS) auf einen regulären Ausdruck, der allen möglichen Zeilenumbrüchen entspricht. Setzen Sie das Trennzeichen für den Ausgabedatensatz ( ORS) auf das Zeilenumbruchzeichen im Unix-Stil.

awk 'BEGIN{RS="\r|\n|\r\n|\n\r";ORS="\n"}{print}' windows_or_macos.txt > unix.txt
kazmer
quelle
Das ist derjenige, der für mich funktioniert hat (MacOS, git diffzeigt ^ M, bearbeitet in vim)
Dorian
2

Unter Linux ist es einfach, ^ M (Strg-M) mit sed in * nix newlines (^ J) zu konvertieren.

Es wird so etwas auf der CLI geben, es wird tatsächlich einen Zeilenumbruch im Text geben. Das \ gibt das ^ J jedoch an sed weiter:

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

Sie erhalten dies, indem Sie bei der Eingabe ^ V (Strg-V), ^ M (Strg-M) und \ (Backslash) verwenden:

sed 's/^V^M/\^V^J/g' < ffmpeg.log > new.log
Jet
quelle
Das hat bei mir funktioniert, danke!
Dan Mantyla
2
sed --expression='s/\r\n/\n/g'

Da in der Frage sed erwähnt wird, ist dies der einfachste Weg, sed zu verwenden, um dies zu erreichen. Der Ausdruck besagt, dass alle Wagenrückläufe und Zeilenvorschübe nur durch Zeilenvorschübe ersetzt werden. Das ist es, was Sie brauchen, wenn Sie von Windows zu Unix wechseln. Ich habe überprüft, ob es funktioniert.

Johannes Paul
quelle
Hey John Paul - diese Antwort wurde zum Löschen markiert und stand für mich in einer Überprüfungswarteschlange. Wenn Sie eine Frage wie diese haben, die 8 Jahre alt ist und 22 Antworten enthält, möchten Sie im Allgemeinen erklären, wie nützlich Ihre Antwort auf eine Weise ist, die andere vorhandene Antworten nicht haben.
Zzxyz
0

Als Erweiterung der Unix-zu-DOS-Lösung von Jonathan Leffler zur sicheren Konvertierung in DOS, wenn Sie sich über die aktuellen Zeilenenden der Datei nicht sicher sind:

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

Dadurch wird überprüft, ob die Zeile nicht bereits in CRLF endet, bevor sie in CRLF konvertiert wird.

Tölpel
quelle
0

Ich habe ein Skript basierend auf der akzeptierten Antwort erstellt, damit Sie es direkt konvertieren können, ohne am Ende eine zusätzliche Datei zu benötigen und diese anschließend zu entfernen und umzubenennen.

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

Stellen Sie einfach sicher, dass bei einer Datei wie "file1.txt" "file1.txt2" noch nicht vorhanden ist oder diese überschrieben wird. Ich verwende diese Datei als temporären Speicherort für die Datei.

OZZIE
quelle
0

Mit Bash 4.2 und neuer können Sie so etwas verwenden, um die nachfolgende CR zu entfernen, die nur Bash-integrierte Funktionen verwendet:

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

Ich habe sed 's / ^ M $ //' file.txt unter OSX sowie verschiedene andere Methoden ausprobiert ( http://www.thingy-ma-jig.co.uk/blog/25-11-2010/fixing- dos-line-endings oder http://hintsforums.macworld.com/archive/index.php/t-125.html ). Keine funktionierte, die Datei blieb unverändert (übrigens wurde Strg-v Enter benötigt, um ^ M zu reproduzieren). Am Ende habe ich TextWrangler verwendet. Es ist nicht streng Befehlszeile, aber es funktioniert und es beschwert sich nicht.

mercergeoinfo
quelle