Aufgrund eines noch nicht diagnostizierten Anwendungsfehlers habe ich mehrere hundert Server mit einer vollen Festplatte. Es gibt eine Datei, die mit doppelten Zeilen gefüllt wurde - keine Protokolldatei, sondern eine Benutzerumgebungsdatei mit Variablendefinitionen (daher kann ich die Datei nicht einfach löschen).
Ich schrieb einen einfachen sed
Befehl, um nach den irrtümlich hinzugefügten Zeilen zu suchen und sie zu löschen, und testete ihn an einer lokalen Kopie der Datei. Es hat wie beabsichtigt funktioniert.
Als ich es jedoch auf dem Server mit der vollständigen Festplatte versuchte, wurde ungefähr der folgende Fehler angezeigt (es stammt aus dem Speicher, nicht aus dem Kopieren und Einfügen):
sed: couldn't flush /path/to/file/sed8923ABC: No space left on deviceServerHostname
Natürlich weiß ich , dass kein Platz mehr vorhanden ist. Deshalb versuche ich Sachen zu löschen! (Der von sed
mir verwendete Befehl reduziert eine Datei mit mehr als 4000 Zeilen auf etwa 90 Zeilen.)
Mein sed
Befehl ist gerechtsed -i '/myregex/d' /path/to/file/filename
Gibt es eine Möglichkeit, diesen Befehl trotz der vollen Festplatte anzuwenden?
(Es muss automatisiert werden, da ich es als schnelle Lösung auf mehrere hundert Server anwenden muss.)
(Natürlich muss der Anwendungsfehler diagnostiziert werden, aber in der Zwischenzeit funktionieren die Server nicht richtig ....)
Update: Die Situation, mit der ich konfrontiert war, wurde gelöst, indem etwas anderes gelöscht wurde, von dem ich herausgefunden habe, dass ich es löschen kann. Ich möchte jedoch weiterhin die Antwort auf diese Frage, die in Zukunft und für andere Personen hilfreich sein würde.
/tmp
ist ein No-Go; Es befindet sich im selben Dateisystem.
Bevor ich Speicherplatz freigegeben habe, habe ich getestet und herausgefunden, dass ich die Zeilen löschen kann, indem ich vi
die Datei öffne :g/myregex/d
und ausführe und die Änderungen dann erfolgreich mit speichere :wq
. Es scheint möglich zu sein, dies zu automatisieren, ohne auf ein separates Dateisystem zurückzugreifen, um eine temporäre Datei zu speichern .... (?)
quelle
sed -i
Erstellt eine temporäre Kopie für die Bearbeitung. Ich vermute, dased
wäre besser dafür, obwohl ich nicht vertraut genug bin, um eine tatsächliche Lösung zu verbietened
Sie ausführen:printf %s\\n g/myregex/d w q | ed -s infile
aber denken Sie daran , dass einige Implementierungen auch temporäre Dateien verwenden, genau wiesed
(Sie könnten Busybox Ed ausprobieren - afaik es erstellt keine temporäre Datei)echo
. verwendenprintf
. undsed
fügen Sie ein Zeichen hinzu, das Sie in der letzten Zeile ablegen, damit Sie nicht nachgestellte Leerzeichen verlieren. Außerdem muss Ihre Shell in der Lage sein, die gesamte Datei in einer einzigen Befehlszeile zu verarbeiten. Das ist dein Risiko - zuerst testen.bash
ist besonders schlecht dabei (ich denke, es ist mit Stapelplatz zu tun?) und kann Sie jederzeit krank machen. Die beidensed
empfohlenen würden zumindest den Pipe-Puffer des Kernels verwenden, um eine gute Wirkung zwischen ihnen zu erzielen, aber die Methode ist ziemlich ähnlich. Ihre Befehlsunterfunktion schneidet auch ab,file
ob das sed w / in erfolgreich ist oder nicht.sed '/regex/!H;$!d;x' <file|{ read v && cat >file;}
und wenn es funktioniert, lies den Rest meiner Antwort. 'Antworten:
Die
-i
Option überschreibt die Originaldatei nicht wirklich. Es erstellt eine neue Datei mit der Ausgabe und benennt sie dann in den ursprünglichen Dateinamen um. Da Sie im Dateisystem keinen Platz für diese neue Datei haben, schlägt dies fehl.Sie müssen dies in Ihrem Skript selbst tun, aber die neue Datei auf einem anderen Dateisystem erstellen.
Wenn Sie nur Zeilen löschen, die einem regulären Ausdruck entsprechen, können Sie
grep
stattdessen anstelle von verwendensed
.Im Allgemeinen ist es für Programme selten möglich, dieselbe Datei als Eingabe und Ausgabe zu verwenden. Sobald das Schreiben in die Datei beginnt, wird der Teil des Programms, der aus der Datei liest, nicht mehr den ursprünglichen Inhalt sehen. Es muss also entweder zuerst die Originaldatei irgendwo kopieren oder in eine neue Datei schreiben und sie umbenennen, wenn sie fertig ist.
Wenn Sie keine temporäre Datei verwenden möchten, können Sie versuchen, den Dateiinhalt im Speicher zwischenzuspeichern:
quelle
rsync -a --no-owner --no-group --remove-source-files "$backupfile" "$destination"
von hiersed -i
dass das Zeug erhalten bleibt?sed -i
bewahrt keines dieser Dinge. Ich habe es gerade mit einer Datei versucht, die ich nicht besitze, die sich jedoch in einem Verzeichnis befindet, das ich besitze, und ich konnte die Datei ersetzen. Der Ersatz gehört mir, nicht dem ursprünglichen Besitzer.var=$(< FILE); echo "$FILE" | grep '^"' > FILE
v=$(<file)&& printf %s\\n "$v" >file
aber noch nicht einmal verwenden&&
. Der Fragesteller spricht davon, es in einem Skript auszuführen - das Überschreiben einer Datei mit einem Teil von sich selbst zu automatisieren. Sie sollten zumindest überprüfen, ob Sie die Ein- und Ausgabe erfolgreich öffnen können. Außerdem kann die Shell explodieren.So
sed
funktioniert es. Bei Verwendung mit-i
(direkt bearbeiten)sed
wird eine temporäre Datei mit dem neuen Inhalt der verarbeiteten Datei erstellt. Wenn Sie fertig sindsed
, wird die aktuelle Arbeitsdatei durch die temporäre ersetzt. Das Dienstprogramm bearbeitet die Datei nicht an Ort und Stelle . Das ist genau das Verhalten jedes Editors.Es ist, als würden Sie die folgende Aufgabe in einer Shell ausführen:
Zu diesem Zeitpunkt
sed
wird versucht, die gepufferten Daten mit demfflush()
Systemaufruf in die in der Fehlermeldung angegebene Datei zu leeren:Für Ihr Problem sehe ich eine Lösung darin, ein separates Dateisystem (z. B. a
tmpfs
, wenn Sie über genügend Speicher oder ein externes Speichergerät verfügen) bereitzustellen und einige Dateien dorthin zu verschieben, dort zu verarbeiten und zurück zu verschieben.quelle
Seit ich diese Frage gestellt habe, habe ich erfahren, dass
ex
es sich um ein POSIX-kompatibles Programm handelt. Es ist fast universell mit verbundenvim
, aber in beiden ist (glaube ich) ein wichtiger Punktex
in Bezug auf Dateisysteme (entnommen aus der POSIX-Spezifikation):"... soll beeinflussen jede Datei aus ..." Ich glaube, dass das Einfügen von Daten in das Dateisystem (überhaupt eine temporäre Datei) als "Auswirkungen auf jede Datei" gelten würde. Könnte sein?*
Eine sorgfältige Untersuchung der POSIX-Spezifikationen
ex
zeigt einige "Fallstricke" hinsichtlich der beabsichtigten tragbaren Verwendung im Vergleich zu gängigen Skriptverwendungen vonex
online gefundenen (die mitvim
spezifischen Befehlen übersät sind ).+cmd
ist gemäß POSIX optional.-c
Optionen ist ebenfalls optional.:g
"frisst" alles bis zur nächsten nicht maskierten Newline (und führt es daher nach jeder Übereinstimmung aus, die für den regulären Ausdruck gefunden wurde, und nicht einmal am Ende).-c 'g/regex/d | x'
Löscht also nur eine Instanz und beendet dann die Datei.Nach meinen Recherchen lautet die POSIX-kompatible Methode zum direkten Bearbeiten einer Datei in einem vollständigen Dateisystem zum Löschen aller Zeilen, die einem bestimmten regulären Ausdruck entsprechen, wie folgt:
Dies sollte funktionieren, vorausgesetzt, Sie haben genügend Speicher, um die Datei in einen Puffer zu laden.
* Wenn Sie etwas finden, das auf etwas anderes hinweist, erwähnen Sie es bitte in den Kommentaren.
quelle
ex +g/match/d -scx file
ist auch POSIX-konform?vi
funktioniert hat , glaube ich, dass es in den meisten Fällen auch funktionieren würdeex
- wenn auch möglicherweise nicht für eine gigantische Datei.sed -i
funktioniert nicht auf einem vollständigen Dateisystem, unabhängig von der Dateigröße.Benutze die Pfeife, Luke!
Datei lesen | Filter | Schreib zurück
In diesem Fall
sed
wird keine neue Datei erstellt und nur eine Ausgabe gesendet, andd
die dieselbe Datei geöffnet wird . Natürlich kann mangrep
im Einzelfall verwendendann die restlichen abschneiden .
quelle
sed
immer temporäre Dateien?grep
sowieso nichtsponge
Befehl zu sein. Ja,sed
mit-i
immer erstellt Dateien lilke "seduyUdmw" mit 000 Rechten.Wie in anderen Antworten erwähnt,
sed -i
wird die Datei in eine neue Datei im selben Verzeichnis kopiert , Änderungen im Prozess vorgenommen und die neue Datei dann über das Original verschoben. Deshalb funktioniert es nicht.ed
(der ursprüngliche Zeileneditor) funktioniert auf ähnliche Weise, aber als ich das letzte Mal nachgesehen habe, wird er/tmp
für die Arbeitsdatei verwendet. Wenn Sie/tmp
sich in einem anderen als dem vollen Dateisystem befinden,ed
erledigen Sie den Job möglicherweise für Sie.Versuchen Sie dies (an Ihrer interaktiven Shell-Eingabeaufforderung):
Das
P
(was ein Kapital P ist) ist nicht unbedingt notwendig. Die Eingabeaufforderung wird aktiviert. Ohne sie arbeiten Sie im Dunkeln, und einige Leute finden das beunruhigend. Diew
undq
sind w rite und q uit.Wenn sich Ihr
/tmp
Verzeichnis auf dem Dateisystem befindet, das voll ist (oder wenn das Dateisystem auch voll ist), versuchen Sie, irgendwo Speicherplatz zu finden. Chaos erwähnt das Mounten eines tmpfs oder eines externen Speichergeräts (z. B. eines Flash-Laufwerks); Wenn Sie jedoch mehrere Dateisysteme haben und diese nicht alle voll sind, können Sie einfach eines der anderen vorhandenen verwenden. Chaos schlägt vor, die Datei (en) in das andere Dateisystem zu kopieren, dort (mitsed
) zu bearbeiten und dann wieder zu kopieren. An diesem Punkt kann dies die einfachste Lösung sein. Eine Alternative wäre jedoch, ein beschreibbares Verzeichnis in einem Dateisystem mit freiem Speicherplatz zu erstellen, die Umgebungsvariable so festzulegenTMPDIR
, dass sie auf dieses Verzeichnis verweist, und dann auszuführened
. (Offenlegung: Ich bin nicht sicher, ob dies funktionieren wird, aber es kann nicht schaden.)Sobald Sie mit der
ed
Arbeit beginnen, können Sie dies automatisierenin einem Skript. Oder , wie von don_crissti vorgeschlagen.
printf '%s\n' 'g/myregex/d' w q | ed -s filename
quelle
ed
oder mitex
) so gemacht werden, dass Speicher anstelle eines separaten Dateisystems verwendet wird? Das war es, was ich wirklich wollte (und der Grund, warum ich keine Antwort akzeptiert habe)ed
vielen Jahren ausgiebig studiert . Es gab immer noch 16-Bit-Computer, auf denen Prozesse auf einen 64-KB-Adressraum (!) Beschränkt waren. Die Idee, dass ein Editor die gesamte Datei in den Speicher liest, war also kein Anfänger. Seitdem ist der Speicher natürlich größer geworden - aber auch Festplatten und Dateien. Da die Festplatten so groß sind, haben die Menschen nicht das Bedürfnis, sich mit der Möglichkeit auseinanderzusetzen/tmp
, dass ihnen der Speicherplatz ausgeht. Ich habe mir nur kurz den Quellcode einer aktuellen Version von angesehened
, und es scheint immer noch… (Fortsetzung)ed
(oderex
odervi
) eine Option bietet, um den Puffer im Speicher zu halten. Auf der anderen Seite sagt Textbearbeitung mit ed und vi - Kapitel 11: Textverarbeitung - Teil II: Erkundung von Red Hat Linux - Red Hat Linux 9 Professional Secrets - Linux-Systeme , dass siched
der Bearbeitungspuffer im Speicher befindet,… (Fortsetzung) )vi
(das ist das gleiche Programm wieex
). Ich glaube, dass sie nur schlampige, ungenaue Formulierungen verwenden - aber wenn es im Internet (oder in gedruckter Form) ist, muss es wahr sein, oder? Sie bezahlen Ihr Geld und treffen Ihre Wahl.Sie können die Datei ganz einfach abschneiden, wenn Sie die Anzahl der Bytes auf Ihren Versatz bringen können und Ihre Linien von einem Startpunkt bis zum Ende verlaufen.
Oder wenn Sie
${TMPDIR:-/tmp}
sich in einem anderen Dateisystem befinden:Weil (die meisten) Shells ihre Here-Dokumente dort in einer gelöschten temporären Datei ablegen. Es ist absolut sicher, solange der
<<FILE
Deskriptor von Anfang bis Ende beibehalten wird und${TMPDIR:-/tmp}
so viel Platz hat, wie Sie benötigen.Shells, die keine temporären Dateien verwenden, verwenden Pipes und können daher auf diese Weise nicht sicher verwendet werden. Diese Schalen sind typischerweise
ash
Derivate wiebusybox
,dash
, BSDsh
-zsh
,bash
,ksh
und die Bourne - Shell jedoch verwenden alle temporären Dateien.Anscheinend habe ich letzten Juli ein kleines Shell-Programm geschrieben , um so etwas zu machen
Wenn dies
/tmp
nicht möglich ist, solange Sie die Datei in den Speicher einfügen können, z.... würde im Allgemeinen zumindest sicherstellen, dass die Datei beim ersten
sed
Prozess vollständig gepuffert wurde , bevor versucht wird, die In / Out-Datei abzuschneiden.Eine gezieltere und effizientere Lösung könnte sein:
... weil es nicht stören würde, Zeilen zu puffern, die Sie sowieso löschen wollten.
Ein Test des allgemeinen Falls:
quelle
/tmp
sich auf demselben Dateisystem befinden. Ich mag deine Dual-sed
Version. Ich denke, eine Kombination aus Barmars und Ihrer Antwort wäre wahrscheinlich die beste, etwa:myvar="$(sed '/myregex/d' < file)" && [ -n "$myvar" ] && echo "$myvar" > file ; unset myvar
(In diesem Fall ist es mir egal, nachfolgende Zeilenumbrüche beizubehalten.)sed
|cat
Das obige Element öffnet niemals die Ausgabe, es sei denn,sed
die gesamte Datei wurde bereits gepuffert und Sie können mit dem Schreiben der gesamten Datei für die Ausgabe beginnen. Wenn es versucht, die Datei zu puffern, und dies fehlschlägt,read
ist dies nicht erfolgreich, da EOF in der|
Pipe gefunden wird, bevor es seine erste neue Zeile liest, und dies daher erstcat >out
geschieht, wenn es vollständig aus dem Speicher geschrieben werden muss. Ein Überlauf oder ähnliches schlägt einfach fehl. Außerdem gibt die gesamte Pipeline jedes Mal Erfolg oder Misserfolg zurück. Das Speichern in einer Var ist nur riskanter.file=$(sed '/regex/!H;$!d;x' <file | read v && tee file) && cmp - file <<<"$file" || shite
würden die Ausgabedatei und die Variable gleichzeitig geschrieben, was entweder eine effektive Sicherung oder eine effektive Sicherung wäre, was der einzige Grund ist, warum Sie möchten komplizieren Sie die Dinge weiter als nötig.read script
undread v
in Ihrer Antwort nicht. Wenn Sie mehr darüber erzählen können, werde ich sehr geschätzt, danke!$script
ist dassed
Skript, mit dem Sie auf den gewünschten Teil Ihrer Datei abzielen würden. Es ist das Skript, mit dem Sie das gewünschte Endergebnis im Stream erhalten.v
ist nur ein Platzhalter für eine leere Zeile. In einerbash
Shell ist dies nicht erforderlich, dabash
die$REPLY
Shell-Variable automatisch an ihrer Stelle verwendet wird, wenn Sie keine angeben. POSIXly sollten Sie dies jedoch immer tun. Ich bin übrigens froh, dass du es nützlich findest. viel Glück damit. Ich bin mikeserv @ gmail, wenn Sie etwas in der Tiefe brauchen. Ich sollte in ein paar Tagen wieder einen Computer habenDiese Antwort leiht Ideen aus dieser anderen Antwort und dieser anderen Antwort aus , baut jedoch darauf auf und schafft eine Antwort, die allgemeiner anwendbar ist:
In der ersten Zeile wird der
sed
Befehl ausgeführt, wobei die Ausgabe in die Standardausgabe (und nicht in eine Datei) geschrieben wird. speziell zu einer Pipe,wc
um die Zeichen zu zählen. Die zweite Zeile läuft auch dassed
Kommando mit Ausgang auf der Standardausgabe geschrieben, die, in diesem Fall auf die Eingabedatei in der Lese- / Schreibüberschreiben umgeleitet wird (NEIN Trunkat) Modus, der diskutiert wird hier . Dies ist eine etwas gefährliche Sache; Es ist nur dann sicher, wenn der Filterbefehl niemals die Datenmenge (Text) erhöht. dh für jedes n Bytes, das es liest, schreibt es n oder weniger Bytes. Dies gilt natürlich für densed '/myregex/d'
Befehl; Für jede Zeile, die es liest, schreibt es genau dieselbe Zeile oder nichts. (Andere Beispiele:s/foo/fu/
oders/foo/bar/
wäre sicher, abers/fu/foo/
unds/foo/foobar/
würde nicht.)Beispielsweise:
weil diese 32 Datenbytes:
wurde mit diesen 25 Zeichen überschrieben:
Lassen Sie die sieben Bytes
night.\n
am Ende übrig.Schließlich
dd
sucht der Befehl bis zum Ende der neuen, bereinigten Daten (in diesem Beispiel Byte 25) und entfernt den Rest der Datei. dh es schneidet die Datei an diesem Punkt ab.Wenn der
1<>
Trick aus irgendeinem Grund nicht funktioniert, können Sie dies tunBeachten Sie außerdem, dass Sie, solange Sie nur Linien entfernen, nur
grep -v myregex
(wie von Barmar hervorgehoben ) benötigen .quelle
sed -i 'd' / path / to / file / filename
quelle