Zeilenenden CRLF und LF: Was dabei wirklich kaputtgeht
CRLF gegen LF ist ein Unterschied von einem einzigen Byte. LF ist ein einzelnes \n (0x0A) und beendet Zeilen unter Linux und macOS. CRLF sind zwei Bytes, \r\n (0x0D 0x0A), und beenden Zeilen unter Windows.
Den nächsten Punkt stellen die meisten Artikel falsch dar: Ein Shell-Skript mit CRLF-Zeilenenden scheitert meistens nicht. Es läuft durch, gibt genau das aus, was Sie erwarten, und endet mit 0. Kaputt ist der Wert, der in einer Variablen sitzt, denn die Zuweisung hat das angehängte \r behalten, und niemand hat sich darüber beschwert.
Gemessen zerfällt das Verhalten in vier Fehlerbilder, sortiert danach, wie schwer sie zu bemerken sind:
- Stiller Erfolg: Die Ausgabe stimmt, eine Variable trägt ein unsichtbares
\r, Exit-Code 0. - Ein Fehler, der nichts ändert:
: command not foundauf einer Leerzeile, danach läuft das Skript bis zum Ende durch, Exit-Code 0. - Syntaxfehler bei
if,forund Funktionsdefinitionen, Exit-Code 2. bad interpreter, weil die Shebang-Zeile das\rträgt, Exit-Code 126.
Führen Sie vor allem anderen file yourfile aus. Eine einzige Ausgabezeile sagt Ihnen, auf welcher Seite von CRLF und LF Sie stehen.
Alle Zahlen unten stammen aus Messungen auf macOS (Darwin arm64) mit bash 3.2.57(1)-release, zsh 5.9, dash, git 2.55.0, node v26.7.0 und Python 3.14.6. Gegengeprüft unter Linux über
docker run --rm bash:5, also GNU bash 5.3.15(1)-release (aarch64-unknown-linux-musl).
1. CRLF, LF und CR: was die Bytes tatsächlich sind
CRLF steht für carriage return line feed, und das sind buchstäblich zwei aneinandergeklebte Zeichen:
| Name | Escape-Sequenz | Byte |
|---|---|---|
| Wagenrücklauf (carriage return) | \r | 0x0D |
| Zeilenvorschub (line feed) | \n | 0x0A |
| CRLF | \r\n | 0x0D 0x0A |
Welche Plattform welches Zeilenende schreibt:
| Plattform | Zeilenende |
|---|---|
| Windows | CRLF (\r\n) |
| Linux, modernes macOS | LF (\n) |
| Mac Classic, OS 9 und älter | CR (\r) allein |
Die Namen kommen aus der Mechanik. Am Fernschreiber schob der Wagenrücklauf den Druckkopf zurück an den linken Rand, und der Zeilenvorschub drehte das Papier eine Zeile weiter. Windows hat beide Bewegungen als zwei Bytes beibehalten; Unix befand eine davon für ausreichend. Einzelnes CR taucht noch in alten Exporten auf, und eine Datei voll davon sieht für die meisten Unix-Werkzeuge wie eine einzige riesige Zeile aus.
Ein Befehl zeigt Ihnen, welches Sie haben
file liest die Bytes und benennt das Zeilenende:
$ file d1.txt
d1.txt: ASCII text, with CRLF line terminators
$ file d2.txt
d2.txt: ASCII text ← no suffix means pure LF
$ file d3.txt
d3.txt: ASCII text, with CRLF, LF line terminators ← mixed, file lists both
Der dritte Fall ist der lehrreiche. Eine Datei mit gemischten Zeilenenden bedeutet, dass zwei Werkzeuge mit unterschiedlichen Einstellungen nacheinander hineingeschrieben haben: Ein Editor hat LF gespeichert, ein Skript hat CRLF angehängt, ein Merge hat zwei Fassungen zusammengenäht. od -c zeigt die Trennung Byte für Byte:
$ od -c d3.txt
0000000 a \r \n b \n
0000005
Zeilenenden sitzen auf der Byte-Ebene, direkt neben der Zeichenkodierung; der Leitfaden zu UTF-8 und UTF-16 behandelt, was eine Ebene darüber passiert.
2. Die vier Fehlerbilder, vom stillen Erfolg bis Exit 126
Dieselbe \r\n-Datei, fünf verschiedene Ergebnisse, je nachdem, was in der Zeile steht:
| Skriptinhalt | Was tatsächlich passiert | Exit |
|---|---|---|
echo hello und andere einfache Befehle | Stiller Erfolg, die Ausgabe stimmt | 0 |
Zuweisung X=abc | Stiller Erfolg, aber der Wert endet auf \r | 0 |
Leerzeilen, Fortsetzungen mit \ am Zeilenende | : command not found, das Skript läuft bis zum Ende weiter | 0 |
if/fi, for/do/done, f() { | syntax error near unexpected token | 2 |
Shebang-Zeile mit \r | bad interpreter: No such file or directory | 126 |
Wegen der ersten beiden Zeilen gibt es diese Seite. „CRLF führt zu command not found“ wird überall nachgeplappert, nur passiert es so nicht. Einfache Befehle beschweren sich überhaupt nicht.
Der stille Erfolg ist der gefährliche
$ printf 'echo hello\r\necho world\r\n' > win.sh && bash win.sh
hello
world
[exit=0]
Zwei Zeilen rein, zwei Zeilen raus, Exit 0. Nichts zu debuggen, und kein Text, nach dem man das Log durchsuchen könnte. Lassen Sie dasselbe Skript einen Vergleich anstellen, und es kippt:
$ printf 'V=1.2.3\r\ntest "$V" = "1.2.3" && echo MATCH || echo NO-MATCH\r\n' > v.sh
$ bash v.sh
NO-MATCH
[exit=0]
$V enthält 1.2.3\r, nicht 1.2.3. Auf macOS und Linux dasselbe Ergebnis. Versionsprüfungen, if [ "$ENV" = "prod" ], aus einer Datei gelesene Feature-Flags: Jede dieser Konstruktionen nimmt lautlos den falschen Zweig, mit Exit-Code 0. Das ist der Zeilenende-Fehler, der sich in CI am schwersten finden lässt, weil der Build grün ist und das Log sauber.
Der Fehler, der nichts aufhält
Eine Leerzeile innerhalb einer CRLF-Datei ist eine Zeile, die nur ein \r enthält, und die Shell behandelt sie als auszuführenden Befehl. Der schlägt fehl, gibt eine Meldung aus, und das Skript geht zur nächsten Zeile über und endet mit Exit 0. Der genaue Wortlaut hängt von Ihrer Shell ab; darum geht es im nächsten Abschnitt.
Fortsetzungszeilen mit abschließendem Backslash brechen auf dieselbe Weise. Das \r steht zwischen dem Backslash und dem Zeilenumbruch, damit hört die Fortsetzung auf, eine Fortsetzung zu sein, und die folgende Zeile läuft für sich allein.
Syntaxfehler, und warum fi nicht fi ist
c_if.sh: line 5: syntax error: unexpected end of file
[exit=2]
Linux-bash 5.3.15 sagt über dieselbe Datei mehr:
i.sh: line 5: syntax error: unexpected end of file from `if' command on line 2
Schleifen und Funktionsdefinitionen scheitern stattdessen schon in Zeile 1:
c_for.sh: line 1: syntax error near unexpected token `do'
c_for.sh: line 1: `for i in 1 2; do'
c_func.sh: line 1: syntax error near unexpected token `{'
c_func.sh: line 1: `f() {'
Der Mechanismus macht die ganze Klasse vorhersagbar. Bash liest das abschließende Wort als fi\r, nicht als fi. fi\r ist nicht das Schlüsselwort fi, also schließt der if-Block nie, und bash liest weiter, bis die Datei zu Ende ist. Deshalb zeigt der Fehler auf die letzte Zeile statt auf die kaputte.
bad interpreter und Exit 126
$ ./s.sh
bash: ./s.sh: /bin/bash^M: bad interpreter: No such file or directory
[exit=126]
macOS und Linux geben denselben Text aus und beenden beide mit 126. Lesen Sie den Pfad in der Meldung: /bin/bash^M. Der Kernel nimmt alles nach #! bis zum Zeilenumbruch als Interpreterpfad, und das \r gehört dazu. Eine solche Datei existiert nicht, also scheitert das exec, bevor eine einzige Zeile Ihres Skripts läuft.
Exit 126 deckt auch „gefunden, aber nicht ausführbar“ ab, ein Skript, das nicht starten will, ist also nicht automatisch ein Zeilenende-Problem; Dateirechte erzeugen Fehler aus derselben Familie. Das ^M im Pfad ist es, was die beiden auseinanderhält.
Warum Sie das \r nie sehen
Die Ausgabe anzusehen hilft nicht, denn das Byte druckt nichts, was man sehen oder markieren könnte. Sie müssen es sichtbar erzwingen:
$ bash d.sh # script: HOST=example.com / echo "connecting to $HOST:8080"
connecting to example.com:8080 ← what you get
$ bash d.sh | cat -v
connecting to example.com^M:8080^M ← what is actually there
$ bash d.sh | od -c
0000000 c o n n e c t i n g t o e x
0000020 a m p l e . c o m \r : 8 0 8 0 \r
0000040 \n
Zwei \r-Bytes, in der normalen Ausgabe unsichtbar, beide innerhalb einer Zeichenkette, die gleich als Hostname verwendet wird. Manchmal wandert das Byte in ein Argument, und ein völlig unbeteiligtes Werkzeug bekommt die Schuld:
$ bash v.sh # the script pipes through: ... | head -2
head: illegal line count -- 2\r
head verhält sich korrekt. Das Skript hat ihm 2\r übergeben. Jede Fehlermeldung mit einem verirrten \r im zitierten Wert ist dieser Fehler unter fremdem Namen.
3. Warum Ihre Fehlermeldung nicht wie die im Netz aussieht
Eine Datei aus printf 'echo a\r\n\r\necho b\r\n', deren Zeile 2 nur ein \r enthält, unter vier Shells ausgeführt:
| Shell | Version | Genaue Meldung |
|---|---|---|
| bash (mit macOS ausgeliefert) | 3.2.57(1)-release | s_blank.sh: line 2: : command not found |
| bash (gängiges Linux) | 5.3.15(1)-release | t.sh: line 2: $'\r': command not found |
| zsh | 5.9 | s_blank.sh:2: command not found: ^M |
| dash | — | s_blank.sh: 2: : not found |
Fast jedes Suchergebnis zitiert die zweite Zeile. Bash 4 und 5 geben nicht druckbare Zeichen im ANSI-C-Quoting aus, was aus dem Wagenrücklauf $'\r' macht. Das mit macOS ausgelieferte bash ist 3.2 und tut das nicht, also bekommen Sie einen Doppelpunkt, ein Leerzeichen und nichts dazwischen. zsh gibt ^M aus. dash lässt das Wort „command“ ganz weg.
Ein Fehler, vier Meldungen. Wenn Sie Ihre exakte Fehlermeldung in ein Suchfeld eingefügt und nichts Brauchbares zurückbekommen haben, liegt es daran. Das nackte : command not found ist derselbe Fehler wie der berühmte.
4. Git: was core.autocrlf wirklich in Ihrem Repository ablegt
Die meisten Erklärungen zu git autocrlf hören bei den Definitionen auf. Interessanter ist, was im Commit landet und was die Kolleginnen und Kollegen beim Auschecken bekommen. Gemessen mit git cat-file -p HEAD:f.txt für den gespeicherten Blob und rm f.txt && git checkout -- f.txt für die Arbeitskopie:
core.autocrlf | Quelldatei | Blob im Repository | Arbeitskopie nach dem Checkout |
|---|---|---|---|
true | CRLF | LF | CRLF |
true | LF | LF | CRLF |
input | CRLF | LF | LF |
input | LF | LF | LF |
false | CRLF | CRLF | CRLF |
false | LF | LF | LF |
Daraus folgt:
trueundinputgarantieren beide LF im Repository. Der einzige Unterschied liegt beim Checkout:truewandelt zurück nach CRLF,inputlässt die Datei in Ruhe.- Nur
falsebringt CRLF in einen Commit. Wenn jemand fragt, wer die Wagenrückläufe eingecheckt hat, ist diese Zeile die Antwort. - Zeile zwei ist die Überraschung. Unter
truekommt eine Datei, die auf der Platte LF war, nach dem Checkout als CRLF zurück.
Git kündigt das Umschreiben vorher an, in einer von zwei Formen:
warning: in the working copy of 'f.txt', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'f.txt', CRLF will be replaced by LF the next time Git touches it
„Ich habe nichts geändert, aber git sagt, die ganze Datei sei geändert“
Wieder Zeile zwei. Mit git config core.autocrlf true schreibt der Checkout LF-Dateien im Arbeitsverzeichnis auf CRLF um. Jede Zeile unterscheidet sich jetzt um ein Byte vom Blob, also meldet git diff jede Zeile als geändert, und der Pull Request zeigt eine Datei, die niemand angefasst hat, als komplett neu geschrieben. Das war der Checkout-Filter.
Das Spiegelbild erzeugt dasselbe Rauschen: Jemand im Team mit false committet CRLF, Sie stehen auf input, und Dateien, die Sie nie geöffnet haben, tauchen in Ihrem Diff auf.
5. .gitattributes ist die Antwort auf Teamebene
core.autocrlf ist eine Einstellung auf genau einem Rechner, für alle anderen unsichtbar. .gitattributes ist eine Datei im Repository und reist deshalb mit jedem Klon mit. Wenn beide sich widersprechen, gewinnen die Attribute. Alle vier Formen getestet:
.gitattributes | core.autocrlf | Quelle | Blob | Nach dem Checkout | Sieger |
|---|---|---|---|---|---|
* text=auto | false | CRLF | LF | LF | Attribute |
* text eol=crlf | input | LF | LF | CRLF | Attribute |
* -text | true | CRLF | CRLF | CRLF | Attribute |
* text eol=lf | true | CRLF | LF | LF | Attribute |
Zeile drei sollte man sich merken: -text schaltet die Umwandlung komplett ab und schlägt trotzdem core.autocrlf=true. So schützen Sie Dateien, deren Bytes unangetastet überleben müssen.
Eine .gitattributes zum Übernehmen
* text=auto
*.sh text eol=lf
*.bash text eol=lf
Makefile text eol=lf
*.bat text eol=crlf
*.cmd text eol=crlf
*.ps1 text eol=crlf
*.png -text
*.jpg -text
*.pdf -text
*.zip -text
text=auto normalisiert alles, was Git als Text erkennt, im Repository auf LF. Die expliziten eol=lf-Zeilen decken Dateien ab, die LF sein müssen, egal wer sie geschrieben hat, denn eine .sh mit CRLF ist ein Exit 126 im Wartestand. Windows-Skripttypen bekommen aus dem spiegelbildlichen Grund eol=crlf, und Binärmuster bekommen -text, damit gar nichts umgewandelt wird.
core.safecrlf und core.eol
Zwei Einstellungen, die neben core.autocrlf auftauchen und etwas anderes tun:
core.safecrlfist ein Wächter, kein Umwandler. Wenn eine Umwandlung nicht verlustfrei hin und zurück ginge – etwa bei einer gemischten Datei, wo das Normalisieren Information verliert –, lehnttruedie Operation ab, undwarnlässt sie mit einer Warnung durch. Es ändert nie, welche Bytes gespeichert werden; es weigert sich nur, die verlustbehafteten Fälle stillschweigend auszuführen.core.eollegt fest, welches Zeilenende Git im Arbeitsverzeichnis für die alstextmarkierten Dateien schreibt, wenncore.autocrlfauffalsesteht. Mögliche Werte sindlf,crlfundnative.core.autocrlfüberstimmt die Einstellung, weshalb ein gesetztescore.eolauf einem Rechner mit weiterhin aktivem autocrlf meist so aussieht, als hätte es nichts bewirkt.
Eine geänderte .gitattributes repariert die bereits eingecheckten Dateien nicht
Attribute greifen, wenn Git eine Datei schreibt oder liest, bestehende Inhalte bleiben also, wie sie sind, bis etwas sie neu schreibt. Erzwingen Sie diesen Durchlauf selbst:
$ git add --renormalize .
$ git commit -m "Normalize line endings"
Rechnen Sie mit einem riesigen Diff, genau darum geht es. Machen Sie das in einem eigenen Branch und mergen Sie es als einen einzigen Commit. Und sagen Sie dem Team vorher Bescheid.
6. CRLF nach LF umwandeln – und zurück
Vier Wege, alle unter Darwin daraufhin geprüft, dass sie das \r entfernen:
| Befehl | Ergebnis |
|---|---|
tr -d '\r' < f > f.out | funktioniert |
perl -pi -e 's/\r\n/\n/g' f | funktioniert |
sed -i '' -e 's/\r$//' f (BSD-Form) | funktioniert |
sed -i -e 's/\r$//' f (GNU-Form, auf macOS ausgeführt) | funktioniert, hinterlässt aber eine Müll-Datei |
Die GNU-sed -i-Falle auf macOS
BSD-sed verlangt hinter -i ein Backup-Suffix. Kopieren Sie ein Linux-Tutorial wörtlich, dann schluckt BSD-sed das -e als eben dieses Suffix. Ihre Änderung passiert trotzdem, und das hier auch:
a5.txt
a5.txt-e
a5.txt-e ist eine Sicherungskopie, entstanden, weil sed das -e als das von Ihnen gewünschte Suffix gelesen hat. Die korrekte macOS-Form übergibt eine explizite leere Zeichenkette: sed -i '' -e 's/\r$//' f. Ein Repository mit eingecheckten -e-Dateien ist ein Repository, in dem jemand einen GNU-Einzeiler auf einem Mac ausgeführt hat.
macOS hat kein dos2unix
command -v dos2unix liefert auf einem unveränderten macOS-System nichts zurück; das Binary kommt über brew install dos2unix. Deshalb scheitert die meistkopierte Antwort im Internet ausgerechnet auf dem Rechner, an dem sehr viele Entwickler sitzen. tr -d '\r' braucht keine Installation und erledigt dieselbe Aufgabe.
In der Gegenrichtung hat unix2dos dasselbe Verfügbarkeitsproblem, und sed -e 's/$/\r/' funktioniert als Ersatz.
Sobald eine Datei wieder sauberes LF hat, lässt sie sich gefahrlos als Liste von Zeilen behandeln. Das zählt überall dort, wo Zeilen als ganze Zeichenketten verglichen werden: Textzeilen sortieren und doppelte Zeilen entfernen lesen value\r und value als zwei verschiedene Zeilen, ein einziger verirrter Wagenrücklauf hebelt die Duplikaterkennung also lautlos aus.
Im Editor
VS Code zeigt das Zeilenende der aktuellen Datei unten rechts in der Statusleiste als CRLF oder LF an, und ein Klick darauf schaltet die Datei um. Die Einstellung files.eol bestimmt die Vorgabe für neue Dateien, und pro Workspace gesetzt hält sie ein gemischtes Team einheitlich. Andere Editoren bieten dieselben zwei Stellschrauben unter anderen Namen. Meist übersieht man, dass die Anzeige pro Datei und die Vorgabeeinstellung zwei getrennte Dinge sind: Die eine zu ändern rührt die andere nicht an.
7. Zeilenenden im Code behandeln
Eine Datei, line1\r\nline2\r\n, über sieben Einstiegspunkte gelesen:
| Einstiegspunkt | Was Sie bekommen | \r |
|---|---|---|
Node fs.readFileSync(f,"utf8") | "line1\r\nline2\r\n" | bleibt |
Node, derselbe Wert + .split("\n") | ["line1\r","line2\r",""] | bleibt in jeder Zeile |
Node readline mit crlfDelay:Infinity | ["line1","line2"] | entfernt |
Python open(f), Standardmodus | 'line1\nline2\n' | umgewandelt |
Python open(f).readlines() | ['line1\n','line2\n'] | umgewandelt |
Python open(f, newline="") | 'line1\r\nline2\r\n' | bleibt |
Python open(f,"rb") | b'line1\r\nline2\r\n' | bleibt |
Diese Tabelle erklärt einen Klassiker unter den Fehlerberichten: In Python funktioniert es, in Node bricht es. Der Standard-Textmodus von Python wendet Universal Newlines an und übersetzt \r\n zu \n, bevor Sie es überhaupt zu sehen bekommen. Node reicht Ihnen die Bytes so weiter, wie sie sind. Falsch ist keines von beiden, aber sobald beide dieselbe Datei lesen, sind sie sich uneins.
rstrip("\n") lässt das \r zurück
original: 'line1\r\n' | rstrip("\n"): 'line1\r' | strip(): 'line1'
rstrip("\n") entfernt genau die Zeichen, die Sie aufgelistet haben, und \r stand nicht auf der Liste. Das Ergebnis ist danach ungleich zu allem, womit es übereinstimmen sollte – das ist die ehrliche Antwort auf „Ich habe es abgeschnitten und es ist immer noch nicht gleich“. Nehmen Sie strip() oder rstrip() ohne Argument, dann verschwindet der gesamte nachgestellte Leerraum einschließlich des Wagenrücklaufs.
Zeilen auf beiden Plattformen sicher trennen
Trennen Sie in JavaScript an einem Muster, das beide Zeilenenden verträgt: text.split(/\r?\n/). Bleiben Sie in Python entweder im Standard-Textmodus und überlassen Sie es den Universal Newlines, oder rufen Sie splitlines() auf, das mit \r\n, \n und einem einzelnen \r zurechtkommt.
Das Schreiben ist die andere Hälfte. Node schreibt die Bytes, die Sie ihm geben, bauen Sie Zeichenketten also mit \n und lassen Sie .gitattributes entscheiden, was auf der Platte landet. Pythons open(path, "w") übersetzt \n in das Zeilenende der Plattform, sofern Sie nicht newline="" übergeben. Genau darum verlangen CSV-Writer dieses Argument.
Ein Wagenrücklauf am Zeilenende und eine Byte Order Mark am Dateianfang sind dieselbe Fehlerklasse von den entgegengesetzten Enden her: ein unsichtbares Byte, das Copy-and-paste überlebt und eine Gleichheitsprüfung zerlegt. Der Leitfaden zur UTF-8-BOM-Fehlersuche behandelt dieses Ende.
8. Wo Zeilenenden sonst noch zubeißen
CSV und Excel. RFC 4180 schreibt CRLF als Datensatztrenner vor, damit ist CSV einer der wenigen Orte, an denen CRLF korrekt und kein Defekt ist. Parser, die für reine LF-Eingaben geschrieben wurden, lassen im letzten Feld jeder Zeile ein \r stehen. Wenn Werte aus einer Umwandlung mit CSV zu JSON richtig aussehen, sich aber falsch vergleichen, prüfen Sie zuerst dieses Byte.
Docker. Eine .sh mit CRLF, die in ein Image kopiert wird, ist Fehlerbild vier. COPY erhält die Bytes, die Shebang-Zeile behält ihr \r, und der Container endet mit 126. Eine einzige Zeile *.sh text eol=lf in .gitattributes verhindert die ganze Klasse.
Diffs und Pull Requests. Eine Datei, die als komplett geändert markiert ist, ohne dass eine Bearbeitung sichtbar wäre, ist der Mechanismus aus Abschnitt 4, angekommen im Code-Review. Ein Vergleich der beiden Fassungen mit Text-Diff bestätigt das in Sekunden, und der Leitfaden zum Text-Diff führt durch das Lesen des Ergebnisses.
Gemischte Dateien. ASCII text, with CRLF, LF line terminators bedeutet zwei Schreiber mit unterschiedlichen Einstellungen. Normalisieren Sie die ganze Datei statt nur der Zeilen, die Sie zufällig bearbeitet haben, sonst ist der nächste Diff genauso verrauscht.
FAQ
Was ist der Unterschied zwischen CRLF und LF?
CRLF sind zwei Bytes, \r\n (0x0D 0x0A). LF ist ein Byte, \n (0x0A). Windows schreibt CRLF, Linux und macOS schreiben LF, und beide markieren das Ende einer Zeile. Im Editor sieht der Text identisch aus; der Unterschied zeigt sich erst in den Bytes, in Zeichenkettenvergleichen und in Diffs.
Mein Skript hat CRLF-Zeilenenden. Warum kommt keine Fehlermeldung?
Weil einfache Befehle das zusätzliche Byte überstehen. echo hello mit angehängtem \r läuft und endet mit 0. Fehler treten nur dort auf, wo der Parser genau hinsieht: bei einer Leerzeile, bei einem Schlüsselwort wie fi oder in der Shebang-Zeile. Zuweisungen sind der gefährliche Fall, denn sie gelingen und speichern das \r in der Variablen.
Was bedeutet $'\r': command not found, und warum sehe ich es nicht?
Es bedeutet, dass die Shell versucht hat, eine Zeile auszuführen, die nur einen Wagenrücklauf enthält. Bash 4 und 5 geben dieses Zeichen im ANSI-C-Quoting aus, daraus wird $'\r'. Das mit macOS ausgelieferte bash 3.2 gibt zwischen den Doppelpunkten nichts aus, und zsh gibt stattdessen ^M aus. Ein Fehler, drei verschiedene Meldungen.
Sollte core.autocrlf auf true, input oder false stehen?
Nehmen Sie input unter Linux und macOS, true unter Windows, und bevorzugen Sie .gitattributes gegenüber beidem. Gemessen speichern true und input beide LF im Repository, und nur false lässt CRLF in einen Commit. true schreibt beim Checkout zusätzlich LF-Dateien in Ihrer Arbeitskopie auf CRLF um.
Wenn .gitattributes und core.autocrlf sich widersprechen, was gewinnt?
.gitattributes gewinnt. Alle vier getesteten Formen – * text=auto, * text eol=crlf, * -text und * text eol=lf – haben den lokalen Wert von core.autocrlf überstimmt. Das ist das Argument dafür, sie einzusetzen: Attribute werden eingecheckt und gelten für alle, während core.autocrlf eine Einstellung pro Rechner ist, die Sie weder sehen noch erzwingen können.
Wie prüfe ich, ob eine Datei CRLF oder LF verwendet?
Führen Sie file yourfile aus. Bei CRLF erscheint ASCII text, with CRLF line terminators, bei reinem LF erscheint ASCII text ganz ohne Zusatz, und eine gemischte Datei ergibt with CRLF, LF line terminators. Für Gewissheit auf Byte-Ebene führen Sie od -c aus und suchen nach einem \r, das vor jedem \n sitzt.
Wann sollte man CRLF tatsächlich verwenden?
Wenn ein Format oder ein Protokoll es verlangt. RFC 4180 definiert CRLF als Datensatztrenner für CSV, und HTTP-Header sowie SMTP tun auf der Leitung dasselbe. Windows-Batch- und PowerShell-Dateien fahren mit CRLF ebenfalls sicherer. Überall sonst – Quellcode, Shell-Skripte, Konfiguration – LF.