Ich habe einige Dateien, bei denen ich den letzten Zeilenumbruch löschen möchte, wenn es sich um das letzte Zeichen in einer Datei handelt. od -c
zeigt mir, dass der von mir ausgeführte Befehl die Datei mit einer nachgestellten neuen Zeile schreibt:
0013600 n t > \n
Ich habe ein paar Tricks mit sed ausprobiert, aber das Beste, was ich mir vorstellen kann, ist, den Trick nicht zu machen:
sed -e '$s/\(.*\)\n$/\1/' abc
Irgendwelche Ideen, wie das geht?
\n
ist in Linux ein ZeichenAntworten:
perl -pe 'chomp if eof' filename >filename2
oder, um die Datei an Ort und Stelle zu bearbeiten:
perl -pi -e 'chomp if eof' filename
[Anmerkung der Redaktion:
-pi -e
war ursprünglich-pie
, aber, wie von mehreren Kommentatoren bemerkt und von @hvd erklärt, funktioniert letzteres nicht.]Dies wurde auf der awk-Website, die ich gesehen habe, als "Perl-Blasphemie" beschrieben.
Aber in einem Test hat es funktioniert.
quelle
chomp
. Und es ist besser als die Datei zu schlürfen.perl -pi -e 'chomp if eof' filename
, um eine Datei direkt zu bearbeiten, anstatt eine temporäre Datei zu erstellenperl -pie 'chomp if eof' filename
-> Perl-Skript "chomp if eof" kann nicht geöffnet werden: Keine solche Datei oder kein solches Verzeichnis;perl -pi -e 'chomp if eof' filename
-> funktioniertSie können die Tatsache nutzen, dass Shell- Befehlsersetzungen nachgestellte Zeilenumbrüche entfernen :
Einfache Form, die in bash, ksh, zsh funktioniert:
printf %s "$(< in.txt)" > out.txt
Tragbare (POSIX-kompatible) Alternative (etwas weniger effizient):
printf %s "$(cat in.txt)" > out.txt
Hinweis:
in.txt
Enden mit mehreren Zeilenumbrüche entfernt der Befehl Substitution alle von ihnen - danke, @Sparhawk. (Es werden keine anderen Leerzeichen als nachgestellte Zeilenumbrüche entfernt.)printf %s
stellt sicher, dass kein Zeilenumbruch an die Ausgabe angehängt wird (dies ist die POSIX-kompatible Alternative zum nicht standardmäßigenecho -n
; siehe http://pubs.opengroup.org/onlinepubs/009696799/utilities/echo.html und https: //unix.stackexchange). com / a / 65819 )Eine Anleitung zu den anderen Antworten :
Wenn Perl verfügbar ist, wählen Sie die akzeptierte Antwort - sie ist einfach und speichereffizient (liest nicht die gesamte Eingabedatei auf einmal).
Andernfalls betrachten Sie die Awk- Antwort von ghostdog74 - sie ist dunkel, aber auch speichereffizient . Ein besser lesbares Äquivalent (POSIX-kompatibel) ist:
awk 'NR > 1 { print prev } { prev=$0 } END { ORS=""; print }' in.txt
END
Block verarbeitet werden kann, wo sie ohne Nachstellen gedruckt wird\n
, da das Trennzeichen für den Ausgabedatensatz (OFS
) auf eine leere Zeichenfolge gesetzt ist.Wenn Sie eine ausführliche, aber schnelle und robuste Lösung suchen , die wirklich direkt bearbeitet wird (im Gegensatz zum Erstellen einer temporären Datei, die dann das Original ersetzt), sollten Sie das Perl-Skript von jrockway in Betracht ziehen .
quelle
Sie können dies mit
head
GNU-Coreutils tun. Es unterstützt Argumente, die sich auf das Ende der Datei beziehen. So lassen Sie die letzte Byte-Verwendung aus:head -c -1
Zum Testen auf eine endende Newline können Sie
tail
und verwendenwc
. Im folgenden Beispiel wird das Ergebnis in einer temporären Datei gespeichert und anschließend das Original überschrieben:if [[ $(tail -c1 file | wc -l) == 1 ]]; then head -c -1 file > file.tmp mv file.tmp file fi
Sie können auch
sponge
von verwendenmoreutils
, um "an Ort und Stelle" zu bearbeiten:[[ $(tail -c1 file | wc -l) == 1 ]] && head -c -1 file | sponge file
Sie können auch eine allgemeine wiederverwendbare Funktion
.bashrc
erstellen, indem Sie diese in Ihre Datei einfügen:# Example: remove-last-newline < multiline.txt function remove-last-newline(){ local file=$(mktemp) cat > $file if [[ $(tail -c1 $file | wc -l) == 1 ]]; then head -c -1 $file > $file.tmp mv $file.tmp $file fi cat $file }
Aktualisieren
Wie von Karl Wilbur in den Kommentaren erwähnt und in Sorentars Antwort verwendet ,
truncate --size=-1
kann er die direktehead -c-1
Bearbeitung ersetzen und unterstützen.quelle
truncate --size=-1
stattdessen verwenden,head -c -1
da sie nur die Größe der Eingabedatei ändert, anstatt die Eingabedatei einzulesen, sie in eine andere Datei zu schreiben und dann das Original durch die Ausgabedatei zu ersetzen.head -c -1
das letzte Zeichen entfernt wird, unabhängig davon, ob es sich um eine neue Zeile handelt oder nicht. Deshalb müssen Sie überprüfen, ob das letzte Zeichen eine neue Zeile ist, bevor Sie es entfernen.head -n -1 abc > newfile tail -n 1 abc | tr -d '\n' >> newfile
Bearbeiten 2:Hier ist eineawk
Version (korrigiert) , die kein potenziell großes Array ansammelt:awk '{if (line) print line; line = $ 0} END {printf $ 0} 'abcquelle
awk
Version. Es dauert zwei Offsets (und einen anderen Test) und ich habe nur einen verwendet. Sie können jedochprintf
anstelle von verwendenORS
.head -n -1 abc | cat <(tail -n 1 abc | tr -d '\n') | ...
gaffen
awk '{q=p;p=$0}NR>1{print q}END{ORS = ""; print p}' file
quelle
awk '{ prev_line = line; line = $0; } NR > 1 { print prev_line; } END { ORS = ""; print line; }' file
Dies sollte leichter zu lesen sein.awk 'NR>1 {print p} {p=$0} END {printf $0}' file
.printf
das ist Format Argument. Wenn die Eingabedatei also etwas hätte, das als Formatbezeichner interpretiert werden könnte%d
, würde eine Fehlermeldung angezeigt. Eine Lösung wäre, es inprintf "%s" $0
Eine sehr einfache Methode für einzeilige Dateien, für die GNU-Echo von coreutils erforderlich ist:
/bin/echo -n $(cat $file)
quelle
\n
vorhanden. Da wird es in eine neue Zeile umgewandelt.$(...)
zitiert wird/bin/echo -n "$(cat infile)"
Ich muss das definitiv zitieren ... Außerdem bin ich mir nicht sicher, wieecho
hoch die maximale Länge oder die Shell in den Betriebssystemen / Shell-Versionen / Distributionen sein würde (ich habe nur gegoogelt und es war ein Kaninchenbau), also bin ich es Ich bin mir nicht sicher, wie portabel (oder performant) es tatsächlich für etwas anderes als kleine Dateien wäre - aber für kleine Dateien großartig.Wenn du es richtig machen willst, brauchst du so etwas:
use autodie qw(open sysseek sysread truncate); my $file = shift; open my $fh, '+>>', $file; my $pos = tell $fh; sysseek $fh, $pos - 1, 0; sysread $fh, my $buf, 1 or die 'No data to read?'; if($buf eq "\n"){ truncate $fh, $pos - 1; }
Wir öffnen die Datei zum Lesen und Anhängen. Öffnen zum Anhängen bedeutet, dass wir bereits
seek
am Ende der Datei sind. Wir erhalten dann die numerische Position des Dateiende mittell
. Wir verwenden diese Zahl, um ein Zeichen zu suchen, und lesen dann dieses eine Zeichen. Wenn es sich um eine neue Zeile handelt, kürzen wir die Datei auf das Zeichen vor dieser neuen Zeile, andernfalls tun wir nichts.Dies läuft in konstanter Zeit und konstantem Speicherplatz für jede Eingabe und erfordert auch keinen weiteren Speicherplatz.
quelle
Eine schnelle Lösung ist die Verwendung des Dienstprogramms gnu
truncate
:[ -z $(tail -c1 file) ] && truncate -s-1 file
Der Test ist wahr, wenn die Datei eine nachfolgende neue Zeile enthält.
Das Entfernen ist sehr schnell, wirklich vorhanden, es wird keine neue Datei benötigt und die Suche liest auch am Ende nur ein Byte (
tail -c1
).quelle
[ -z $(tail -c1 filename) ] && truncate -s -1 filename
(auch als Antwort auf den anderen Kommentartruncate
funktioniert der Befehl nicht mit stdin, ein Dateiname ist erforderlich)Hier ist eine schöne, ordentliche Python-Lösung. Ich habe nicht versucht, hier knapp zu werden.
Dadurch wird die Datei an Ort und Stelle geändert, anstatt eine Kopie der Datei zu erstellen und die neue Zeile aus der letzten Zeile der Kopie zu entfernen. Wenn die Datei groß ist, ist dies viel schneller als die Perl-Lösung, die als beste Antwort ausgewählt wurde.
Es schneidet eine Datei um zwei Bytes ab, wenn die letzten zwei Bytes CR / LF sind, oder um ein Byte, wenn das letzte Byte LF ist. Es wird nicht versucht, die Datei zu ändern, wenn die letzten Bytes nicht (CR) LF sind. Es behandelt Fehler. Getestet in Python 2.6.
Fügen Sie dies in eine Datei mit dem Namen "striplast" und ein
chmod +x striplast
.#!/usr/bin/python # strip newline from last line of a file import sys def trunc(filename, new_len): try: # open with mode "append" so we have permission to modify # cannot open with mode "write" because that clobbers the file! f = open(filename, "ab") f.truncate(new_len) f.close() except IOError: print "cannot write to file:", filename sys.exit(2) # get input argument if len(sys.argv) == 2: filename = sys.argv[1] else: filename = "--help" # wrong number of arguments so print help if filename == "--help" or filename == "-h" or filename == "/?": print "Usage: %s <filename>" % sys.argv[0] print "Strips a newline off the last line of a file." sys.exit(1) try: # must have mode "b" (binary) to allow f.seek() with negative offset f = open(filename, "rb") except IOError: print "file does not exist:", filename sys.exit(2) SEEK_EOF = 2 f.seek(-2, SEEK_EOF) # seek to two bytes before end of file end_pos = f.tell() line = f.read() f.close() if line.endswith("\r\n"): trunc(filename, end_pos) elif line.endswith("\n"): trunc(filename, end_pos + 1)
PS Im Geiste von "Perl Golf" ist hier meine kürzeste Python-Lösung. Es schlürft die gesamte Datei von der Standardeingabe in den Speicher, entfernt alle Zeilenumbrüche am Ende und schreibt das Ergebnis in die Standardausgabe. Nicht so knapp wie der Perl; Sie können Perl einfach nicht für kleine knifflige schnelle Sachen wie diese schlagen.
Entfernen Sie das "\ n" aus dem Aufruf von
.rstrip()
und es wird der gesamte Leerraum vom Ende der Datei entfernt, einschließlich mehrerer Leerzeilen.Fügen Sie dies in "slurp_and_chomp.py" ein und führen Sie es aus
python slurp_and_chomp.py < inputfile > outputfile
.import sys sys.stdout.write(sys.stdin.read().rstrip("\n"))
quelle
Noch ein Perl WTDI:
perl -i -p0777we's/\n\z//' filename
quelle
Siehe auch Übereinstimmen mit einem beliebigen Zeichen (einschließlich Zeilenumbrüchen) in sed .
quelle
tr -d '\n'
Verwenden von dd:
file='/path/to/file' [[ "$(tail -c 1 "${file}" | tr -dc '\n' | wc -c)" -eq 1 ]] && \ printf "" | dd of="${file}" seek=$(($(stat -f "%z" "${file}") - 1)) bs=1 count=1 #printf "" | dd of="${file}" seek=$(($(wc -c < "${file}") - 1)) bs=1 count=1
quelle
perl -pi -e 's/\n$// if(eof)' your_file
quelle
g
oder die Klammern nicht erforderlich sindeof
:perl -pi -e 's/\n$// if eof' your_file
.Angenommen, der Unix-Dateityp und Sie möchten nur den letzten Zeilenumbruch, funktioniert dies.
sed -e '${/^$/d}'
Es funktioniert nicht bei mehreren Zeilenumbrüchen ...
* Funktioniert nur, wenn die letzte Zeile eine leere Zeile ist.
quelle
sed
Lösung, die auch für eine nicht leere letzte Zeile funktioniertDies ist eine gute Lösung, wenn Sie sie benötigen, um mit Pipes / Umleitungen zu arbeiten, anstatt sie aus oder in eine Datei zu lesen / auszugeben. Dies funktioniert mit einzelnen oder mehreren Zeilen. Es funktioniert, ob es einen nachgestellten Zeilenumbruch gibt oder nicht.
# with trailing newline echo -en 'foo\nbar\n' | sed '$s/$//' | head -c -1 # still works without trailing newline echo -en 'foo\nbar' | sed '$s/$//' | head -c -1 # read from a file sed '$s/$//' myfile.txt | head -c -1
Einzelheiten:
head -c -1
schneidet das letzte Zeichen der Zeichenfolge ab, unabhängig davon, um welches Zeichen es sich handelt. Wenn die Zeichenfolge also nicht mit einer neuen Zeile endet, verlieren Sie ein Zeichen.sed '$s/$//'
. Das erste$
Mittel wendet den Befehl nur auf die letzte Zeile an.s/$//
bedeutet, das "Ende der Zeile" durch "nichts" zu ersetzen, was im Grunde nichts bedeutet. Es hat jedoch den Nebeneffekt, dass eine nachfolgende neue Zeile hinzugefügt wird, sofern keine vorhanden ist.Hinweis: Die Standardeinstellung von Mac
head
unterstützt diese-c
Option nicht. Sie können stattdessen tunbrew install coreutils
und verwendenghead
.quelle
Noch eine Antwort FTR (und mein Favorit!): Echo / Katze das Ding, das Sie entfernen und die Ausgabe durch Backticks erfassen möchten. Die letzte Zeile wird entfernt. Zum Beispiel:
# Sadly, outputs newline, and we have to feed the newline to sed to be portable echo thingy | sed -e 's/thing/sill/' # No newline! Happy. out=`echo thingy | sed -e 's/thing/sill/'` printf %s "$out" # Similarly for files: file=`cat file_ending_in_newline` printf %s "$file" > file_no_newline
quelle
POSIX SED:
'$ {/ ^ $ / d}'
$ - match last line { COMMANDS } - A group of commands may be enclosed between { and } characters. This is particularly useful when you want a group of commands to be triggered by a single address (or address-range) match.
quelle
echo -en 'a\nb\n' | sed '${/^$/d}'
wird nichts entfernt.echo -en 'a\nb\n\n' | sed '${/^$/d}'
wird entfernt, da die gesamte letzte Zeile leer ist.Das einzige Mal, dass ich dies tun wollte, war für Code Golf, und dann habe ich meinen Code einfach aus der Datei kopiert und in eine
echo -n 'content'>file
Anweisung eingefügt .quelle
sed ':a;/^\n*$/{$d;N;};/\n$/ba' file
quelle
Ich hatte ein ähnliches Problem, arbeitete aber mit einer Windows-Datei und musste diese CRLF behalten - meine Lösung unter Linux:
sed 's/\r//g' orig | awk '{if (NR>1) printf("\r\n"); printf("%s",$0)}' > tweaked
quelle
sed -n "1 x;1 !H $ {x;s/\n*$//p;} " YourFile
Sollte das letzte Vorkommen von \ n in der Datei entfernen. Funktioniert nicht mit großen Dateien (aufgrund der Einschränkung des Sed-Puffers)
quelle
Rubin:
ruby -ne 'print $stdin.eof ? $_.strip : $_'
oder:
ruby -ane 'q=p;p=$_;puts q if $.>1;END{print p.strip!}'
quelle