Wie füge ich eine neue Zeile am Ende einer Datei hinzu?

190

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?

k0pernikus
quelle
1
siehe auch / q / 10082204/155090
RubyTuesdayDONO
1
Gute Lösung, die alle Dateien rekursiv bereinigt. Antwort von @Patrick Oscity
Qwerty
Zukünftig haben Texteditoren häufig Optionen, um sicherzustellen, dass es eine nachgestellte Zeile gibt, die Sie und Ihre Mitarbeiter verwenden können, um sauber zu bleiben.
Nick T

Antworten:

44

Um ein Projekt rekursiv zu bereinigen, verwende ich diesen Oneliner:

git ls-files -z | while IFS= read -rd '' f; do tail -c1 < "$f" | read -r _ || echo >> "$f"; done

Erläuterung:

  • git ls-files -zlistet 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 Sie find -print0 ...betroffene Dateien mit ähnlichen Programmen NULauflisten. 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.

Patrick Oscity
quelle
Sie können dies auch so tun, wenn Sie nur einen Teil Ihrer Dateien bereinigen möchten:find -name \*.java | while read f; do tail -n1 $f | read -r _ || echo >> $f; done
Per Lundberg
@StéphaneChazelas gute Vorschläge, werden versuchen, dies in meine Antwort aufzunehmen.
Patrick Oscity
@PerLundberg Sie können auch ein Muster übergeben, git ls-filesdas Sie vor dem Bearbeiten von Dateien bewahrt, die in der Versionskontrolle nicht erfasst werden.
Patrick Oscity
@ StéphaneChazelas Das Hinzufügen des 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 -dOption zu readist in POSIX sh nicht verfügbar.
Patrick Oscity
Ja, daher mein zsh / bash . Siehe auch meine Verwendung von 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.
Stéphane Chazelas
202

Hier gehts :

sed -i -e '$a\' file

Und alternativ für OS X sed:

sed -i '' -e '$a\' file

Dies wird nur dann \nam 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:

$ cd "$(mktemp -d)"
$ printf foo > test.txt
$ sed -e '$a\' test.txt > test-with-eol.txt
$ diff test*
1c1
< foo
\ No newline at end of file
---
> foo
$ echo $?
1
$ sed -e '$a\' test-with-eol.txt > test-still-with-one-eol.txt
$ diff test-with-eol.txt test-still-with-one-eol.txt
$ echo $?
0
l0b0
quelle
1
@jwd: Von man sed: $ Match the last line.Aber vielleicht funktioniert es nur durch Zufall. Ihre Lösung funktioniert auch.
l0b0
1
Ihre Lösung ist auch eleganter, und ich habe sie getestet und festgeschrieben , aber wie kann sie funktionieren? Wenn es $mit der letzten Zeile übereinstimmt, warum fügt es einer Zeichenfolge, die bereits eine neue Zeile enthält, keine neue Zeile hinzu?
20.
27
Es gibt zwei verschiedene Bedeutungen von $. 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.
jwd
1
In Bezug auf die Manpage: Das Zitat, auf das Sie sich beziehen, befindet sich im Abschnitt "Adressen". Wenn Sie es hineinlegen, /regex/erhält es eine andere Bedeutung. Die FreeBSD-Hilfeseiten sind meiner Meinung nach ein wenig informativer: freebsd.org/cgi/man.cgi?query=sed
jwd
2
Wenn die Datei bereits in einer neuen Zeile endet, wird sie dadurch nicht geändert, jedoch neu geschrieben und der Zeitstempel aktualisiert. Das mag oder mag nicht wichtig sein.
Keith Thompson
39

Guck mal:

$ echo -n foo > foo 
$ cat foo
foo$
$ echo "" >> foo
$ cat foo
foo

so echo "" >> noeol-filesollte es tun. (Oder wollten Sie darum bitten, diese Dateien zu identifizieren und zu reparieren?)

edit entfernte den ""von echo "" >> foo(siehe @ yuyichaos Kommentar) edit2 fügte den ""erneut hinzu ( aber siehe @Keith Thompsons Kommentar)

sr_
quelle
4
das ""ist nicht notwendig (zumindest für bash) und tail -1 | wc -lkann verwendet werden, um die datei ohne neue
zeile
5
@ yuyichao: Das ""ist nicht notwendig für bash, aber ich habe echoImplementierungen gesehen , die nichts ausgeben , wenn sie ohne Argumente aufgerufen werden (obwohl keine von denen, die ich jetzt finde, dies tun). echo "" >> noeol-fileist wahrscheinlich etwas robuster. printf "\n" >> noeol-fileist noch mehr so.
Keith Thompson
2
@KeithThompson, csh‚s echoist derjenige Ausgang nichts bekannt , wenn kein Argument übergeben. Aber wenn wir nicht-Bourne-artige Shells unterstützen wollen, sollten wir es schaffen, echo ''anstatt echo ""wie echo ""es zum Beispiel ""<newline>mit rcoder ausgegeben würde es.
Stéphane Chazelas
1
@ StéphaneChazelas: Und tcshim Gegensatz dazu cshwird beim Aufrufen ohne Argumente eine neue Zeile gedruckt - unabhängig von der Einstellung von $echo_style.
Keith Thompson
16

Eine andere Lösung mit ed. Diese Lösung wirkt sich nur auf die letzte Zeile aus und nur, wenn \nFolgendes fehlt:

ed -s file <<< w

Das Öffnen der Datei zum Bearbeiten funktioniert im Wesentlichen über ein Skript. Das Skript ist der einzige wBefehl, mit dem die Datei auf die Festplatte zurückgeschrieben wird. Es basiert auf diesem Satz in der ed(1)Manpage:

EINSCHRÄNKUNGEN
       (...)

       Wenn eine Textdatei (nicht binär) nicht durch ein Zeilenvorschubzeichen abgeschlossen wird,
       Dann fügt ed einen zum Lesen / Schreiben hinzu. Im Falle einer Binärdatei
       file, ed fügt beim Lesen / Schreiben keine neue Zeile hinzu.
Enzotib
quelle
1
Dies fügt mir keine neue Zeile hinzu.
Olhovsky
4
Funktioniert bei mir; Es wird sogar "Newline angehängt" ausgegeben (ed-1.10-1 unter Arch Linux).
Stefan Majewsky
12

Eine einfache, tragbare, POSIX-kompatible Möglichkeit, einer Textdatei einen fehlenden, endgültigen Zeilenumbruch hinzuzufügen, ist:

[ -n "$(tail -c1 file)" ] && echo >> file

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.

Barfuß IO
quelle
1
Beachten Sie, dass dies nicht funktioniert, yashwenn 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).
Stéphane Chazelas
1
@ StéphaneChazelas Eine Lösung für Yash wurde hinzugefügt .
Sorontar
1
Ist es möglich, dies für jede Datei in einem Ordner und Unterordnern auszuführen?
Qwerty
12

Neue Zeile hinzufügen, unabhängig davon:

echo >> filename

Hier ist eine Möglichkeit, mit Python zu überprüfen, ob am Ende eine neue Zeile vorhanden ist, bevor eine hinzugefügt wird:

f=filename; python -c "import sys; sys.exit(open(\"$f\").read().endswith('\n'))" && echo >> $f
Alexander
quelle
1
Ich würde die Python-Version wegen der langsamen Python-Startzeit in keiner Schleife verwenden. Natürlich könnten Sie die Schleife in Python machen, wenn Sie wollten.
Kevin Cox
2
Die Startzeit für Python beträgt hier 0,03 Sekunden. Halten Sie das wirklich für problematisch?
Alexander
3
Die Startzeit spielt eine Rolle, wenn Sie Python in einer Schleife aufrufen. Deshalb habe ich gesagt, Sie sollten die Schleife in Python ausführen. Dann fallen die Anlaufkosten nur noch einmal an. Für mich ist die Hälfte der Kosten für das Startup mehr als die Hälfte der Zeit des gesamten Snipits. Ich würde diesen erheblichen Overhead in Betracht ziehen. (Wiederum irrelevant, wenn nur eine kleine Anzahl von Dateien)
Kevin Cox
2
echo ""scheint robuster als echo -n '\n'. Oder Sie könntenprintf '\n'
Keith Thompson
2
Das hat gut funktioniert für mich
Daniel Gomez Rico
8

Die schnellste Lösung ist:

[ -n "$(tail -c1 file)" ] && printf '\n' >>file 

  1. Ist echt schnell.
    Bei einer Datei mittlerer Größe seq 99999999 >filedauert dies Millisekunden.
    Andere Lösungen brauchen viel Zeit:

    [ -n "$(tail -c1 file)" ] && printf '\n' >>file  0.013 sec
    vi -ecwq file                                    2.544 sec
    paste file 1<> file                             31.943 sec
    ed -s file <<< w                             1m  4.422 sec
    sed -i -e '$a\' file                         3m 20.931 sec
  2. Funktioniert mit ash, bash, lksh, mksh, ksh93, attsh und zsh, aber nicht mit yash.

  3. Ändert den Zeitstempel der Datei nicht, wenn keine neue Zeile hinzugefügt werden muss.
    Alle anderen hier vorgestellten Lösungen ändern den Zeitstempel der Datei.
  4. Alle oben genannten Lösungen sind gültige POSIX-Lösungen.

Wenn Sie eine Lösung benötigen, die portabel ist (und alle anderen oben aufgelisteten Shells), wird sie möglicherweise etwas komplexer:

f=file
if       [ "$(tail -c1 "$f"; echo x)" != "$(printf '\nx')" ]
then     printf '\n' >>"$f"
fi
Isaac
quelle
7

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:

tail -c1 file | xxd -u -p
tail -c1 file | hexdump -v -e '/1 "%02X"'

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 0Anatürlich kein letztes Zeichen liefert von:

f=file
a=$(tail -c1 "$f" | hexdump -v -e '/1 "%02X"')
[ -s "$f" -a "$a" != "0A" ] && echo >> "$f"

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.

Sorontar
quelle
1
@crw Ich glaube, dass es nützliche Informationen hinzufügt.
Sorontar
2
Beachten Sie, dass es weder POSIX-Dienstprogramme xxdnoch hexdumpsolche gibt. Im POSIX-Werkzeugkasten od -An -tx1muss der Hex-Wert eines Bytes ermittelt werden.
Stéphane Chazelas
@ StéphaneChazelas Bitte poste das als Antwort; Ich bin hierher gekommen und habe zu oft nach diesem Kommentar gesucht :)
Kelvin
@kelvin, ich habe meine Antwort
Stéphane Chazelas
Beachten Sie, dass POSIX nicht garantiert, dass der Wert von LF 0x0a ist. Es gibt immer noch POSIX-Systeme, bei denen dies nicht der Fall ist (EBCDIC-basierte Systeme), obwohl dies heutzutage äußerst selten ist.
Stéphane Chazelas
4

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 ...?

AD7six
quelle
2
Vim ist der betreffende Herausgeber. Aber im Allgemeinen haben Sie Recht, ich sollte nicht nur die Symptome beheben;)
k0pernikus
6
Für vim müssen Sie sich sehr viel Mühe geben, um vim zu veranlassen , am Ende der Datei keine neue Zeile einzufügen. Tun Sie diesen Tanz einfach nicht. ODER, um vorhandene Dateien einfach zu korrigieren, öffnen Sie sie in vim und speichern Sie die Datei. Vim 'repariert' den fehlenden Zeilenumbruch für Sie (kann einfach für mehrere Dateien geskriptet werden)
AD7six
3
Ich emacsfüge keine neue Zeile am Ende der Datei hinzu.
Enzotib
2
Vielen Dank für den Kommentar @ AD7six, ich bekomme immer wieder Phantomberichte von Diffs, wenn ich Dinge festschreibe, wie die Originaldatei am Ende keinen Zeilenumbruch hat. Egal, wie ich eine Datei mit vim bearbeite, ich kann es nicht schaffen, dort keine neue Zeile einzufügen. Also ist es nur vim es zu tun.
Steven Lu
1
@enzotib: Ich habe (setq require-final-newline 'ask)in meinem.emacs
Keith Thompson
3

Wenn Sie beim Verarbeiten einer Pipeline nur schnell eine neue Zeile hinzufügen möchten, gehen Sie wie folgt vor:

outputting_program | { cat ; echo ; }

Es ist auch POSIX-konform.

Dann können Sie es natürlich in eine Datei umleiten.

MichalH
quelle
2
Die Tatsache, dass ich dies in einer Pipeline verwenden kann, ist hilfreich. Auf diese Weise kann ich die Anzahl der Zeilen in einer CSV-Datei ohne den Header zählen. Und es hilft, eine genaue Zeilenanzahl für Windows-Dateien zu erhalten, die nicht mit einem Zeilenumbruch oder einem Zeilenumbruch enden. cat file.csv | tr "\r" "\n" | { cat; echo; } | sed "/^[[:space:]]*$/d" | tail -n +2 | wc -l
Kyle Tolle
3

Vorausgesetzt, die Eingabe enthält keine Nullen:

paste - <>infile >&0

... 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.

Toby Speight
quelle
Das wird nicht so funktionieren, da stdin und stdout dieselbe offene Dateibeschreibung haben (also Cursor innerhalb der Datei). Du würdest paste infile 1<> infilestattdessen brauchen .
Stéphane Chazelas
2

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.

find . -type f | # sort |        # sort file names if you like
/usr/bin/perl -lne '
   open FH, "<", $_ or do { print " error: $_"; next };
   $pos = sysseek FH, 0, 2;                     # seek to EOF
   if (!defined $pos)     { print " error: $_"; next }
   if ($pos == 0)         { print " empty: $_"; next }
   $pos = sysseek FH, -1, 1;                    # seek to last char
   if (!defined $pos)     { print " error: $_"; next }
   $cnt = sysread FH, $c, 1;
   if (!$cnt)             { print " error: $_"; next }
   if ($c eq "\n")        { print "   EOL: $_"; next }
   else                   { print "no EOL: $_"; next }
'

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:

find . -type f -print0 | sort -z |
/usr/bin/perl -ln0e '
   open FH, "<", $_ or do { print " error: $_"; next };
   $pos = sysseek FH, 0, 2;                     # seek to EOF
   if (!defined $pos)     { print " error: $_"; next }
   if ($pos == 0)         { print " empty: $_"; next }
   $pos = sysseek FH, -1, 1;                    # seek to last char
   if (!defined $pos)     { print " error: $_"; next }
   $cnt = sysread FH, $c, 1;
   if (!$cnt)             { print " error: $_"; next }
   if ($c eq "\n")        { print "   EOL: $_"; next }
   else                   { print "no EOL: $_"; next }
'

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

 echo >> "$filename"

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:

/usr/bin/perl -ne 'print "$ARGV\n" if /.\z/' -- FILE1 FILE2 ...
jrw32982
quelle
1

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:

vi -ecwq foo.txt

was äquivalent ist zu:

ex -cwq foo.txt

Testen:

$ printf foo > foo.txt && wc foo.txt
0 1 3 foo.txt
$ ex -scwq foo.txt && wc foo.txt
1 1 4 foo.txt

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 .

Kenorb
quelle
0

So wenden Sie die akzeptierte Antwort auf alle Dateien im aktuellen Verzeichnis (plus Unterverzeichnisse) an:

$ find . -type f -exec sed -i -e '$a\' {} \;

Dies funktioniert unter Linux (Ubuntu). Unter OS X müssen Sie wahrscheinlich -i ''(ungetestet) verwenden.

friederblümle
quelle
4
Beachten Sie, dass find .alle Dateien aufgelistet werden, einschließlich der Dateien in .git. find . -type f -not -path './.git/*' -exec sed -i -e '$a\' {} \;
Auszuschließen
Ich wünschte, ich hätte diesen Kommentar gelesen / darüber nachgedacht, bevor ich ihn lief. Naja.
kstev
0

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

 mv file old; grep '' <old >file; touch -r old file

(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.

dave_thompson_085
quelle
Oder einfach grep '' file 1<> file, obwohl das die Datei immer noch vollständig lesen und schreiben würde.
Stéphane Chazelas
-1

Dies funktioniert in AIX ksh:

lastchar=`tail -c 1 *filename*`
if [ `echo "$lastchar" | wc -c` -gt "1" ]
then
    echo "/n" >> *filename*
fi

In meinem Fall gibt der wcBefehl einen Wert von zurück , wenn in der Datei die neue Zeile fehlt, 2und wir schreiben eine neue Zeile.

Daemoncan
quelle
Feedback wird in Form von Up- oder Downvotes gegeben, oder Sie werden in Kommentaren gebeten, Ihre Antworten / Fragen genauer zu skizzieren. Halten Sie es auf den Punkt, herzlich willkommen bei stackexchange!
k0pernikus
-1

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.

Chiffre
quelle
-1

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.

user247137
quelle
Der StackExchange hat eine lustige Formatierung, ich habe sie für dich
korrigiert
-1

Wenn Ihre Datei mit Windows- Zeilenenden abgeschlossen ist \r\nund Sie sich in Linux befinden, können Sie diesen sedBefehl verwenden. Es wird nur \r\nzur letzten Zeile hinzugefügt , wenn es nicht bereits vorhanden ist:

sed -i -e '$s/\([^\r]\)$/\1\r\n/'

Erläuterung:

-i    replace in place
-e    script to run
$     matches last line of a file
s     substitute
\([^\r]\)$    search the last character in the line which is not a \r
\1\r\n    replace it with itself and add \r\n

Wenn die letzte Zeile bereits ein enthält, \r\nstimmt der reguläre Ausdruck der Suche nicht überein, daher geschieht nichts.

masgo
quelle
-1

Sie könnten ein fix-non-delimited-lineSkript schreiben wie:

#! /bin/zsh -
zmodload zsh/system || exit
ret=0
for file do
  if sysopen -rwu0 -- "$file"; then
    if sysseek -w end -1; then
      read -r x || print -u0
    else
      syserror -p "Can't seek in $file before the last byte: "
      ret=1
    fi
  else
    ret=1
  fi
done
exit $ret

Im Gegensatz zu einigen der hier angegebenen Lösungen ist es

  • sollte insofern effizient sein, als es keinen Prozess auslöst, nur ein Byte für jede Datei liest und die Datei nicht überschreibt (nur eine neue Zeile anfügt)
  • unterbricht keine Symlinks / Hardlinks oder beeinflusst Metadaten (auch die ctime / mtime werden nur aktualisiert, wenn eine neue Zeile hinzugefügt wird)
  • sollte auch dann funktionieren, wenn das letzte Byte eine NUL ist oder Teil eines Mehrbytezeichens ist.
  • sollte einwandfrei funktionieren, unabhängig davon, welche Zeichen oder Nichtzeichen die Dateinamen enthalten dürfen
  • Sollte korrekt mit nicht lesbaren oder nicht beschreibbaren oder nicht suchbaren Dateien umgehen (und Fehler dementsprechend melden)
  • Sollte keine neue Zeile zu leeren Dateien hinzufügen (meldet aber einen Fehler über eine ungültige Suche in diesem Fall)

Sie können es zum Beispiel verwenden als:

that-script *.txt

oder:

git ls-files -z | xargs -0 that-script

POSIXly könnten Sie etwas tun, das mit funktionell gleichwertig ist

export LC_ALL=C
ret=0
for file do
  [ -s "$file" ] || continue
  {
    c=$(tail -c 1 | od -An -vtc)
    case $c in
      (*'\n'*) ;;
      (*[![:space:]]*) printf '\n' >&0 || ret=$?;;
      (*) ret=1;; # tail likely failed
    esac
  } 0<> "$file" || ret=$? # record failure to open
done
Stéphane Chazelas
quelle