it-swarm.com.de

Wie kann ich das letzte Zeichen einer Datei in Unix entfernen?

Angenommen, ich habe eine beliebige mehrzeilige Textdatei:

sometext
moretext
lastline

Wie kann ich nur das letzte Zeichen (das e, nicht die Newline oder die Null) der Datei entfernen, ohne die Textdatei ungültig zu machen?

53
MaxPRafferty

Bei einem einfacheren Ansatz ( gibt stdout aus, die Eingabedatei wird nicht aktualisiert):

sed '$ s/.$//' somefile
  • $ Ist eine Sed Adresse, die nur mit der letzten Eingabezeile übereinstimmt, wodurch der folgende Funktionsaufruf (s/.$//) Nur in der letzten Zeile ausgeführt wird.
  • s/.$// Ersetzt das letzte Zeichen in der (in diesem Fall letzten ) Zeile durch eine leere Zeichenfolge. entfernt effektiv das letzte Zeichen. (vor dem Zeilenumbruch) in der Zeile.
    . Stimmt mit einem beliebigen Zeichen in der Zeile überein, und mit $ Wird die Übereinstimmung am Zeilenende verankert. Beachten Sie, dass die Verwendung von $ in diesem regulären Ausdruck konzeptionell verwandt ist, sich jedoch technisch von der vorherigen Verwendung von $ als Sed Adresse .
  • Beispiel mit stdin-Eingabe (setzt Bash, Ksh oder Zsh voraus):

    $ sed '$ s/.$//' <<< $'line one\nline two'
    line one
    line tw
    

Um auch die Eingabedatei zu aktualisieren (nicht verwenden, wenn die Eingabedatei ein Symlink ist):

sed -i '$ s/.$//' somefile

Hinweis:
* Unter OSX müssten Sie -i '' Anstelle von nur -i Verwenden. Eine Übersicht über die mit -i verbundenen Fallstricke finden Sie in der unteren Hälfte von meine Antwort hier .
* Wenn Sie sehr große Eingabedateien verarbeiten müssen und/oder Leistung/Datenträgernutzung ein Problem darstellen und , verwenden Sie [~ # ~] gnu [~ # ~] Dienstprogramme (Linux), siehe sorontars hilfreiche Antwort .

70
mklement0

truncate

truncate -s-1 file

Entfernt ein Zeichen (-1) vom Ende derselben Datei. Genau wie ein >> wird an dieselbe Datei angehängt.

Das Problem bei diesem Ansatz besteht darin, dass er keine nachgestellte Zeile enthält, wenn sie existiert.

Die Lösung ist:

if     [ -n "$(tail -c1 file)" ]    # if the file has not a trailing new line.
then
       truncate -s-1 file           # remove one char as the question request.
else
       truncate -s-2 file           # remove the last two characters
       echo "" >> file              # add the trailing new line back
fi

Das funktioniert, weil tail das letzte Byte nimmt (nicht char).

Selbst bei großen Dateien dauert es fast keine Zeit.

Warum nicht sed

Das Problem bei einer sed-Lösung wie sed '$ s/.$//' file ist, dass zuerst die gesamte Datei gelesen wird (dies dauert bei großen Dateien sehr lange). Dann benötigen Sie eine temporäre Datei (in derselben Größe wie das Original):

sed '$ s/.$//' file  > tempfile
rm file; mv tempfile file

Verschieben Sie dann die empfangene Datei, um die Datei zu ersetzen. 

34
sorontar

Hier ist eine andere Verwendung von ex, die ich nicht so kryptisch finde wie die sed-Lösung:

 printf '%s\n' '$' 's/.$//' wq | ex somefile

Der $ geht zur letzten Zeile, die s löscht das letzte Zeichen und wq ist das bekannte (für vi Benutzer) write + quit.

4
Jens

Wenn das Ziel darin besteht, das letzte Zeichen in der letzten Zeile zu entfernen, sollte diese awk Folgendes tun:

awk '{a[NR]=$0} END {for (i=1;i<NR;i++) print a[i];sub(/.$/,"",a[NR]);print a[NR]}' file
sometext
moretext
lastlin

Es speichert alle Daten in einem Array, druckt es aus und ändert die letzte Zeile.

2
Jotne

Nur eine Anmerkung: sed wird die Datei vorübergehend entfernen. Wenn Sie also die Datei aushaken, wird die Warnung "Keine solche Datei oder ein solches Verzeichnis" angezeigt, bis Sie den Befehl tail erneut ausgeben.

1
Tim Chaubet

BEARBEITETE ANTWORT

Ich habe ein Skript erstellt und Ihren Text auf meinem Desktop abgelegt. Diese Testdatei wird als "old_file.txt" gespeichert

sometext
moretext
lastline

Danach habe ich ein kleines Skript geschrieben, um die alte Datei zu nehmen und das letztes Zeichen in der letzten Zeile zu entfernen

#!/bin/bash
no_of_new_line_characters=`wc  '/root/Desktop/old_file.txt'|cut -d ' ' -f2`
let "no_of_lines=no_of_new_line_characters+1"
sed -n 1,"$no_of_new_line_characters"p  '/root/Desktop/old_file.txt' > '/root/Desktop/my_new_file'
sed -n "$no_of_lines","$no_of_lines"p '/root/Desktop/old_file.txt'|sed 's/.$//g' >> '/root/Desktop/my_new_file'

das Öffnen der von mir erstellten new_file zeigte die Ausgabe wie folgt:

sometext
moretext
lastlin

Ich entschuldige mich für meine vorherige Antwort (habe nicht sorgfältig gelesen)

1
repzero

Nach einer ganzen Reihe von Strategien mit verschiedenen Strategien (und Vermeidung von sed -i oder Perl) fand ich dies am besten mit:

sed '$! { P; D; }; s/.$//' somefile
1
MaxPRafferty

sed 's/.$//' filename | tee newFilename

Das sollte deine Arbeit machen.

0
karthik339