Bei der Verwendung von Versionskontrollsystemen ärgere ich mich über das Geräusch, wenn der Diff sagt No newline at end of file
.
Also habe ich mich gefragt: Wie füge ich eine neue Zeile am Ende einer Datei hinzu, um diese Nachrichten loszuwerden?
bash
shell
text-processing
newlines
k0pernikus
quelle
quelle
Antworten:
Um ein Projekt rekursiv zu bereinigen, verwende ich diesen Oneliner:
Erläuterung:
git ls-files -z
listet Dateien im Repository auf. Als zusätzlichen Parameter wird ein optionales Muster verwendet, das in einigen Fällen nützlich sein kann, wenn Sie den Vorgang auf bestimmte Dateien / Verzeichnisse beschränken möchten. Alternativ können Siefind -print0 ...
betroffene Dateien mit ähnlichen ProgrammenNUL
auflisten. Vergewissern Sie sich nur, dass sie durch -begrenzte Einträge enthalten.while IFS= read -rd '' f; do ... done
Durchläuft die Einträge und behandelt sicher Dateinamen, die Leerzeichen und / oder Zeilenumbrüche enthalten.tail -c1 < "$f"
Liest das letzte Zeichen aus einer Datei.read -r _
Beendet mit einem Exit-Status ungleich Null, wenn eine nachgestellte Zeile fehlt.|| echo >> "$f"
Hängt eine neue Zeile an die Datei an, wenn der Beendigungsstatus des vorherigen Befehls ungleich Null war.quelle
find -name \*.java | while read f; do tail -n1 $f | read -r _ || echo >> $f; done
git ls-files
das Sie vor dem Bearbeiten von Dateien bewahrt, die in der Versionskontrolle nicht erfasst werden.IFS=
Trennzeichens zum Deaktivieren des Trennzeichens ist gut, um das umgebende Leerzeichen beizubehalten. Die nullterminierten Einträge sind nur relevant, wenn Sie Dateien oder Verzeichnisse mit einem Zeilenvorschub im Namen haben, was irgendwie weit hergeholt zu sein scheint, aber die korrektere Art ist, mit dem generischen Fall umzugehen, stimme ich zu. Nur eine kleine Einschränkung: Die-d
Option zuread
ist in POSIX sh nicht verfügbar.tail -n1 < "$f"
, um Probleme mit Dateinamen zu vermeiden, die mit beginnen-
(tail -n1 -- "$f"
funktioniert nicht für die aufgerufene Datei-
). Möglicherweise möchten Sie klarstellen, dass die Antwort jetzt zsh / bash-spezifisch ist.Hier gehts :
Und alternativ für OS X
sed
:Dies wird nur dann
\n
am Ende der Datei hinzugefügt, wenn sie nicht bereits mit einem Zeilenumbruch endet. Wenn Sie es also zweimal ausführen, wird keine neue Zeile hinzugefügt:quelle
man sed
:$ Match the last line.
Aber vielleicht funktioniert es nur durch Zufall. Ihre Lösung funktioniert auch.$
mit der letzten Zeile übereinstimmt, warum fügt es einer Zeichenfolge, die bereits eine neue Zeile enthält, keine neue Zeile hinzu?$
. In einem regulären Ausdruck, wie z. B. dem Formular/<regex>/
, hat es die übliche Bedeutung "Zeilenende abgleichen". Andernfalls gibt sed als Adresse die spezielle Bedeutung "Letzte Zeile in Datei" an. Der Code funktioniert, da sed standardmäßig eine neue Zeile an die Ausgabe anfügt, sofern diese noch nicht vorhanden ist. Der Code "$ a \" sagt nur "Entspricht der letzten Zeile der Datei und fügt nichts hinzu." Aber implizit fügt sed die neue Zeile zu jeder verarbeiteten Zeile (wie dieser$
Zeile) hinzu, wenn sie nicht bereits vorhanden ist./regex/
erhält es eine andere Bedeutung. Die FreeBSD-Hilfeseiten sind meiner Meinung nach ein wenig informativer: freebsd.org/cgi/man.cgi?query=sedGuck mal:
so
echo "" >> noeol-file
sollte es tun. (Oder wollten Sie darum bitten, diese Dateien zu identifizieren und zu reparieren?)edit entfernte den
""
vonecho "" >> foo
(siehe @ yuyichaos Kommentar) edit2 fügte den""
erneut hinzu ( aber siehe @Keith Thompsons Kommentar)quelle
""
ist nicht notwendig (zumindest für bash) undtail -1 | wc -l
kann verwendet werden, um die datei ohne neue""
ist nicht notwendig für bash, aber ich habeecho
Implementierungen gesehen , die nichts ausgeben , wenn sie ohne Argumente aufgerufen werden (obwohl keine von denen, die ich jetzt finde, dies tun).echo "" >> noeol-file
ist wahrscheinlich etwas robuster.printf "\n" >> noeol-file
ist noch mehr so.csh
‚secho
ist derjenige Ausgang nichts bekannt , wenn kein Argument übergeben. Aber wenn wir nicht-Bourne-artige Shells unterstützen wollen, sollten wir es schaffen,echo ''
anstattecho ""
wieecho ""
es zum Beispiel""<newline>
mitrc
oder ausgegeben würdees
.tcsh
im Gegensatz dazucsh
wird beim Aufrufen ohne Argumente eine neue Zeile gedruckt - unabhängig von der Einstellung von$echo_style
.Eine andere Lösung mit
ed
. Diese Lösung wirkt sich nur auf die letzte Zeile aus und nur, wenn\n
Folgendes fehlt:Das Öffnen der Datei zum Bearbeiten funktioniert im Wesentlichen über ein Skript. Das Skript ist der einzige
w
Befehl, mit dem die Datei auf die Festplatte zurückgeschrieben wird. Es basiert auf diesem Satz in dered(1)
Manpage:quelle
Eine einfache, tragbare, POSIX-kompatible Möglichkeit, einer Textdatei einen fehlenden, endgültigen Zeilenumbruch hinzuzufügen, ist:
Bei diesem Ansatz muss nicht die gesamte Datei gelesen werden. es kann einfach nach EOF suchen und von dort aus arbeiten.
Dieser Ansatz muss auch keine temporären Dateien hinter Ihrem Rücken erstellen (z. B. sed -i), sodass Hardlinks nicht betroffen sind.
echo fügt der Datei nur dann eine neue Zeile hinzu, wenn das Ergebnis der Befehlsersetzung eine nicht leere Zeichenfolge ist. Beachten Sie, dass dies nur passieren kann, wenn die Datei nicht leer ist und das letzte Byte kein Zeilenumbruch ist.
Wenn das letzte Byte der Datei ein Zeilenumbruch ist, gibt tail ihn zurück, und die Befehlsersetzung entfernt ihn. Das Ergebnis ist eine leere Zeichenfolge. Der -n-Test schlägt fehl und das Echo wird nicht ausgeführt.
Wenn die Datei leer ist, ist das Ergebnis der Befehlsersetzung ebenfalls eine leere Zeichenfolge, und das Echo wird erneut nicht ausgeführt. Dies ist wünschenswert, da eine leere Datei weder eine ungültige Textdatei noch eine nicht leere Textdatei mit einer leeren Zeile ist.
quelle
yash
wenn das letzte Zeichen in der Datei ein Mehrbytezeichen ist (z. B. in UTF-8-Ländereinstellungen) oder wenn das Gebietsschema C ist und für das letzte Byte in der Datei das 8. Bit festgelegt ist. Bei anderen Shells (mit Ausnahme von zsh) wird keine neue Zeile hinzugefügt, wenn die Datei mit einem NUL-Byte endet (dies bedeutet jedoch, dass die Eingabe auch nach dem Hinzufügen einer neuen Zeile kein Text ist).Neue Zeile hinzufügen, unabhängig davon:
Hier ist eine Möglichkeit, mit Python zu überprüfen, ob am Ende eine neue Zeile vorhanden ist, bevor eine hinzugefügt wird:
quelle
echo ""
scheint robuster alsecho -n '\n'
. Oder Sie könntenprintf '\n'
Die schnellste Lösung ist:
Ist echt schnell.
Bei einer Datei mittlerer Größe
seq 99999999 >file
dauert dies Millisekunden.Andere Lösungen brauchen viel Zeit:
Funktioniert mit ash, bash, lksh, mksh, ksh93, attsh und zsh, aber nicht mit yash.
Alle anderen hier vorgestellten Lösungen ändern den Zeitstempel der Datei.
Wenn Sie eine Lösung benötigen, die portabel ist (und alle anderen oben aufgelisteten Shells), wird sie möglicherweise etwas komplexer:
quelle
Der schnellste Weg, um zu testen, ob das letzte Byte einer Datei ein Zeilenumbruch ist, besteht darin, nur das letzte Byte zu lesen. Das könnte man mit machen
tail -c1 file
. Die vereinfachte Methode zum Testen, ob es sich bei dem Bytewert um eine neue Zeile handelt, schlägt jedoch fehl, je nachdem, wie in der Shell eine nachfolgende neue Zeile in einer Befehlserweiterung normalerweise entfernt wird. 8 wert.Die richtige, POSIX-konforme, alle (vernünftigen) Shells-Methode, um festzustellen, ob das letzte Byte einer Datei eine neue Zeile ist, besteht darin, entweder xxd oder hexdump zu verwenden:
Wenn Sie dann die Ausgabe von oben mit vergleichen
0A
, erhalten Sie einen robusten Test.Es ist hilfreich, zu vermeiden, dass einer ansonsten leeren Datei eine neue Zeile hinzugefügt wird.
Datei, die
0A
natürlich kein letztes Zeichen liefert von:Kurz und bündig. Dies nimmt sehr wenig Zeit in Anspruch, da nur das letzte Byte gelesen wird (Suche nach EOF). Es spielt keine Rolle, ob die Datei groß ist. Fügen Sie dann bei Bedarf nur ein Byte hinzu.
Keine temporären Dateien benötigt oder verwendet. Es sind keine Hardlinks betroffen.
Wenn dieser Test zweimal ausgeführt wird, wird keine neue Zeile hinzugefügt.
quelle
xxd
nochhexdump
solche gibt. Im POSIX-Werkzeugkastenod -An -tx1
muss der Hex-Wert eines Bytes ermittelt werden.Am besten korrigieren Sie den Editor des Benutzers, der die Datei zuletzt bearbeitet hat. Wenn Sie der letzte sind, der die Datei bearbeitet hat - welchen Editor verwenden Sie, nehme ich an, Textmate ...?
quelle
emacs
füge keine neue Zeile am Ende der Datei hinzu.(setq require-final-newline 'ask)
in meinem.emacs
Wenn Sie beim Verarbeiten einer Pipeline nur schnell eine neue Zeile hinzufügen möchten, gehen Sie wie folgt vor:
Es ist auch POSIX-konform.
Dann können Sie es natürlich in eine Datei umleiten.
quelle
cat file.csv | tr "\r" "\n" | { cat; echo; } | sed "/^[[:space:]]*$/d" | tail -n +2 | wc -l
Vorausgesetzt, die Eingabe enthält keine Nullen:
... würde ausreichen, eine neue Zeile immer nur an das Ende einer Infile anzuhängen, wenn noch keine vorhanden wäre. Und es muss nur die Eingabedatei einmal durchlesen, um es richtig zu machen.
quelle
paste infile 1<> infile
stattdessen brauchen .Obwohl es die Frage nicht direkt beantwortet, ist hier ein verwandtes Skript, das ich geschrieben habe, um Dateien zu erkennen, die nicht in newline enden. Es ist sehr schnell.
Das Perl-Skript liest eine Liste von (optional sortierten) Dateinamen aus stdin und liest für jede Datei das letzte Byte, um festzustellen, ob die Datei in einer neuen Zeile endet oder nicht. Dies ist sehr schnell, da nicht der gesamte Inhalt jeder Datei gelesen werden muss. Es gibt für jede gelesene Datei eine Zeile mit dem Präfix "error:" aus, wenn ein Fehler auftritt, "empty:", wenn die Datei leer ist (endet nicht mit newline!), "EOL:" ("end of line ") wenn die Datei mit newline endet und" no EOL: "wenn die Datei nicht mit newline endet.
Hinweis: Das Skript verarbeitet keine Dateinamen, die Zeilenumbrüche enthalten. Wenn Sie auf einem GNU- oder BSD-System arbeiten, können Sie alle möglichen Dateinamen behandeln, indem Sie -print0 zu find, -z zu sortieren und -0 zu perl hinzufügen:
Natürlich müssten Sie sich noch eine Methode einfallen lassen, um die Dateinamen mit Zeilenumbrüchen in der Ausgabe zu codieren (als Übung für den Leser).
Falls gewünscht, kann die Ausgabe gefiltert werden, um eine neue Zeile an diejenigen Dateien anzuhängen, die keine haben, am einfachsten mit
Das Fehlen einer letzten Zeile kann zu Fehlern in Skripten führen, da einige Versionen von Shell und anderen Dienstprogrammen eine fehlende letzte Zeile beim Lesen einer solchen Datei nicht richtig behandeln.
Nach meiner Erfahrung wird das Fehlen einer letzten Zeile durch die Verwendung verschiedener Windows-Dienstprogramme zum Bearbeiten von Dateien verursacht. Ich habe noch nie gesehen, dass vim beim Bearbeiten einer Datei einen fehlenden abschließenden Zeilenumbruch verursacht, obwohl er über solche Dateien berichtet.
Schließlich gibt es viel kürzere (aber langsamere) Skripte, die ihre Dateinameneingaben durchlaufen können, um die Dateien zu drucken, die nicht mit Zeilenvorschub enden, wie zum Beispiel:
quelle
Die
vi
/vim
/ex
-Editoren fügen<EOL>
bei EOF automatisch etwas hinzu, es sei denn, die Datei enthält es bereits.Also versuchen Sie entweder:
was äquivalent ist zu:
Testen:
Um mehrere Dateien zu korrigieren, überprüfen Sie: Wie behebe ich "Keine Zeilenumbrüche am Ende der Datei" für viele Dateien? bei SO
Warum ist das so wichtig? Damit unsere Dateien POSIX-kompatibel bleiben .
quelle
So wenden Sie die akzeptierte Antwort auf alle Dateien im aktuellen Verzeichnis (plus Unterverzeichnisse) an:
Dies funktioniert unter Linux (Ubuntu). Unter OS X müssen Sie wahrscheinlich
-i ''
(ungetestet) verwenden.quelle
find .
alle Dateien aufgelistet werden, einschließlich der Dateien in.git
.find . -type f -not -path './.git/*' -exec sed -i -e '$a\' {} \;
Zumindest in den GNU - Versionen, einfach
grep ''
oderawk 1
Kanonisiert seine Eingabe, das Hinzufügen eine abschließende Newline falls nicht bereits vorhanden. Sie kopieren die Datei während des Vorgangs, was einige Zeit in Anspruch nimmt, wenn sie groß ist (der Quellcode sollte jedoch nicht zu groß sein, um gelesen zu werden?) Und aktualisieren die Mod-Zeit, sofern Sie nicht etwas Ähnliches tun(Obwohl dies für eine Datei in Ordnung sein kann, die Sie einchecken, weil Sie sie geändert haben), und es verliert Hardlinks, nicht standardmäßige Berechtigungen und ACLs usw., es sei denn, Sie sind noch vorsichtiger.
quelle
grep '' file 1<> file
, obwohl das die Datei immer noch vollständig lesen und schreiben würde.Dies funktioniert in AIX ksh:
In meinem Fall gibt der
wc
Befehl einen Wert von zurück , wenn in der Datei die neue Zeile fehlt,2
und wir schreiben eine neue Zeile.quelle
Als Ergänzung zu Patrick Oscitys Antwort können Sie auch Folgendes verwenden , wenn Sie sie nur auf ein bestimmtes Verzeichnis anwenden möchten:
find -type f | while read f; do tail -n1 $f | read -r _ || echo >> $f; done
Führen Sie dies in dem Verzeichnis aus, zu dem Sie neue Zeilen hinzufügen möchten.
quelle
echo $'' >> <FILE_NAME>
fügt am Ende der Datei eine leere Zeile ein.echo $'\n\n' >> <FILE_NAME>
Fügt am Ende der Datei 3 Leerzeilen ein.quelle
Wenn Ihre Datei mit Windows- Zeilenenden abgeschlossen ist
\r\n
und Sie sich in Linux befinden, können Sie diesensed
Befehl verwenden. Es wird nur\r\n
zur letzten Zeile hinzugefügt , wenn es nicht bereits vorhanden ist:Erläuterung:
Wenn die letzte Zeile bereits ein enthält,
\r\n
stimmt der reguläre Ausdruck der Suche nicht überein, daher geschieht nichts.quelle
Sie könnten ein
fix-non-delimited-line
Skript schreiben wie:Im Gegensatz zu einigen der hier angegebenen Lösungen ist es
Sie können es zum Beispiel verwenden als:
oder:
POSIXly könnten Sie etwas tun, das mit funktionell gleichwertig ist
quelle