In-Place-Zeilenlöschung im vollständigen Dateisystem?

11

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 sedBefehl, 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 sedmir verwendete Befehl reduziert eine Datei mit mehr als 4000 Zeilen auf etwa 90 Zeilen.)

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

/tmpist 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 vidie Datei öffne :g/myregex/dund 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 .... (?)

Platzhalter
quelle
Siehe auch
Wildcard
1
sed -iErstellt eine temporäre Kopie für die Bearbeitung. Ich vermute, das edwäre besser dafür, obwohl ich nicht vertraut genug bin, um eine tatsächliche Lösung zu verbieten
Eric Renouf
2
Mit würden edSie ausführen: printf %s\\n g/myregex/d w q | ed -s infileaber denken Sie daran , dass einige Implementierungen auch temporäre Dateien verwenden, genau wie sed(Sie könnten Busybox Ed ausprobieren - afaik es erstellt keine temporäre Datei)
don_crissti
1
@ Wildcard - nicht zuverlässig w / echo. verwenden printf. und sedfü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. bashist besonders schlecht dabei (ich denke, es ist mit Stapelplatz zu tun?) und kann Sie jederzeit krank machen. Die beiden sedempfohlenen 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, fileob das sed w / in erfolgreich ist oder nicht.
Mikeserv
1
@Wildcard - versuche es sed '/regex/!H;$!d;x' <file|{ read v && cat >file;}und wenn es funktioniert, lies den Rest meiner Antwort. '
Mikeserv

Antworten:

10

Die -iOption ü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 grepstattdessen anstelle von verwenden sed.

grep -v 'myregex' /path/to/filename > /tmp/filename && mv /tmp/filename /path/to/filename

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:

file=$(< /path/to/filename)
echo "$file" | grep -v 'myregex' > /path/to/filename
Barmar
quelle
1
Hat es Berechtigungen, Besitz und Zeitstempel bewahrt? Vielleicht rsync -a --no-owner --no-group --remove-source-files "$backupfile" "$destination"von hier
Hastur
@Hastur - willst du damit andeuten, sed -idass das Zeug erhalten bleibt?
Mikeserv
2
@ Hastur sed -ibewahrt 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.
Barmar
1
@ RalphRönnquist Um sicher zu sein, müssten Sie es in zwei Schritten tun:var=$(< FILE); echo "$FILE" | grep '^"' > FILE
Barmar
1
@Barmar - Sie funktionieren nicht - Sie wissen nicht einmal, dass Sie die Eingabe erfolgreich geöffnet haben. Die sehr Mindeste , was Sie tun können , ist v=$(<file)&& printf %s\\n "$v" >fileaber 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.
Mikesserv
4

So sedfunktioniert es. Bei Verwendung mit -i(direkt bearbeiten) sedwird eine temporäre Datei mit dem neuen Inhalt der verarbeiteten Datei erstellt. Wenn Sie fertig sind sed, 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:

sed 'whatever' file >tmp_file
mv tmp_file file

Zu diesem Zeitpunkt sedwird versucht, die gepufferten Daten mit dem fflush()Systemaufruf in die in der Fehlermeldung angegebene Datei zu leeren:

Erzwingt für Ausgabestreams fflush()ein Schreiben aller im Benutzerbereich gepufferten Daten für den angegebenen Ausgabe- oder Aktualisierungsstrom über die zugrunde liegende Schreibfunktion des Streams.


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.

Chaos
quelle
3

Seit ich diese Frage gestellt habe, habe ich erfahren, dass exes sich um ein POSIX-kompatibles Programm handelt. Es ist fast universell mit verbundenvim , aber in beiden ist (glaube ich) ein wichtiger Punkt exin Bezug auf Dateisysteme (entnommen aus der POSIX-Spezifikation):

In diesem Abschnitt wird der Begriff verwendet Editierpuffers das aktuelle Arbeits Text zu beschreiben. Dieser Begriff impliziert keine spezifische Implementierung. Alle Bearbeitungsänderungen werden im Bearbeitungspuffer ausgeführt, und keine Änderungen daran wirken sich auf eine Datei aus, bis ein Editorbefehl die Datei schreibt.

"... 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-Spezifikationenex zeigt einige "Fallstricke" hinsichtlich der beabsichtigten tragbaren Verwendung im Vergleich zu gängigen Skriptverwendungen vonex online gefundenen (die mit vimspezifischen Befehlen übersät sind ).

  1. Die Implementierung +cmdist gemäß POSIX optional.
  2. Mehrere zulassen -c Optionen ist ebenfalls optional.
  3. Der globale Befehl :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:

ex -sc 'g/myregex/d
x' /path/to/file/filename

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.

Platzhalter
quelle
2
aber ex schreibt an tmpfiles ... immer. Es sollte seine Puffer regelmäßig auf die Festplatte schreiben. Es gibt sogar spezifizierte Befehle zum Lokalisieren der tmp-Dateipuffer auf der Festplatte.
Mikeserv
@Wildcard Danke fürs Teilen, ich habe auf einen ähnlichen Beitrag bei SO zurück verlinkt . Ich nehme an, ex +g/match/d -scx fileist auch POSIX-konform?
Kenorb
@kenorb, nicht ganz, gemäß meiner Lektüre der Spezifikationen - siehe meinen Punkt 1 in der obigen Antwort. Das genaue Zitat von POSIX lautet: "Das Ex-Dienstprogramm muss den Syntaxrichtlinien des XBD-Dienstprogramms entsprechen, mit Ausnahme der nicht spezifizierten Verwendung von '-', und dass '+' sowohl als Optionsbegrenzer als auch als '-' erkannt werden kann ."
Wildcard
1
Ich kann es nicht beweisen, außer durch Berufung auf den gesunden Menschenverstand, aber ich glaube, dass Sie mehr in diese Aussage aus der Spezifikation lesen, als wirklich da ist. Ich schlage vor, dass die sicherere Interpretation darin besteht, dass keine Änderungen am Bearbeitungspuffer Auswirkungen auf Dateien haben, die vor Beginn der Bearbeitungssitzung vorhanden waren oder die der Benutzer benannt hat. Siehe auch meine Kommentare zu meiner Antwort.
G-Man sagt "Reinstate Monica"
@ G-Man, ich denke tatsächlich, du hast recht; Meine anfängliche Interpretation war wahrscheinlich Wunschdenken. Da das Bearbeiten der Datei in einem vollständigen Dateisystem vi funktioniert hat , glaube ich, dass es in den meisten Fällen auch funktionieren würde ex- wenn auch möglicherweise nicht für eine gigantische Datei. sed -ifunktioniert nicht auf einem vollständigen Dateisystem, unabhängig von der Dateigröße.
Wildcard
2

Benutze die Pfeife, Luke!

Datei lesen | Filter | Schreib zurück

sed 's/PATTERN//' BIGFILE | dd of=BIGFILE conv=notrunc

In diesem Fall sedwird keine neue Datei erstellt und nur eine Ausgabe gesendet, an dddie dieselbe Datei geöffnet wird . Natürlich kann man grepim Einzelfall verwenden

grep -v 'PATTERN' BIGFILE | dd of=BIGFILE conv=notrunc

dann die restlichen abschneiden .

dd if=/dev/null of=BIGFILE seek=1 bs=BYTES_OF_SED_OUTPUT
Leben Gleben
quelle
1
Haben Sie den Teil "Volles Dateisystem" der Frage bemerkt ?
Wildcard
1
@ Wildcard, verwendet sedimmer temporäre Dateien? grepsowieso nicht
Leben Gleben
Dies scheint eine Alternative zum spongeBefehl zu sein. Ja, sedmit -iimmer erstellt Dateien lilke "seduyUdmw" mit 000 Rechten.
Pablo A
1

Wie in anderen Antworten erwähnt, sed -iwird 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 /tmpfür die Arbeitsdatei verwendet. Wenn Sie /tmpsich in einem anderen als dem vollen Dateisystem befinden, ederledigen Sie den Job möglicherweise für Sie.

Versuchen Sie dies (an Ihrer interaktiven Shell-Eingabeaufforderung):

$ ed / path / to / file / filename
P.
g / myregex / d
w
q

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. Die wund qsind w rite und q uit.

edist berüchtigt für kryptische Diagnose. Wenn an irgendeinem Punkt zeigt es etwas anderes , dass die Eingabeaufforderung (die *) oder etwas , das eindeutig eine Bestätigung des erfolgreichen Betriebes ist ( vor allem , wenn es ein enthält ?), nicht die Datei schreiben (mit w). Beenden Sie einfach ( q). Wenn es Sie nicht rauslässt, versuchen Sie es noch qeinmal.

Wenn sich Ihr /tmpVerzeichnis 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 (mit sed) 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 festzulegen TMPDIR, dass sie auf dieses Verzeichnis verweist, und dann auszuführen ed. (Offenlegung: Ich bin nicht sicher, ob dies funktionieren wird, aber es kann nicht schaden.)

Sobald Sie mit der edArbeit beginnen, können Sie dies automatisieren

ed Dateiname << EOF
g / myregex / d
w
q
EOF

in einem Skript. Oder , wie von don_crissti vorgeschlagen.printf '%s\n' 'g/myregex/d' w q | ed -s filename

G-Man sagt "Reinstate Monica"
quelle
Hmmm. Kann dasselbe (entweder mit edoder mit ex) 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)
Wildcard
Hmm. Dies kann komplizierter sein, als mir klar wurde. Ich habe die Quelle vor edvielen 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 angesehen ed, und es scheint immer noch… (Fortsetzung)
G-Man sagt 'Reinstate Monica' am
(Fortsetzung)… um den „Bearbeitungspuffer“ bedingungslos als temporäre Datei zu implementieren - und ich kann keinen Hinweis darauf finden, dass eine Version von ed(oder exoder vi) 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 sich edder Bearbeitungspuffer im Speicher befindet,… (Fortsetzung) )
G-Man sagt "Reinstate Monica"
(Fortsetzung)… und UNIX Document Processing and Typesetting von Balasubramaniam Srinivasan sagt dasselbe über 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.
G-Man sagt "Reinstate Monica"
Trotzdem habe ich eine neue Antwort hinzugefügt.
G-Man sagt "Reinstate Monica"
1

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.

o=$(sed -ne'/regex/q;p' <file|wc -c)
dd if=/dev/null of=file bs="$o" seek=1

Oder wenn Sie ${TMPDIR:-/tmp}sich in einem anderen Dateisystem befinden:

{   cut -c2- | sed "$script" >file
} <file <<FILE
$(paste /dev/null -)
FILE

Weil (die meisten) Shells ihre Here-Dokumente dort in einer gelöschten temporären Datei ablegen. Es ist absolut sicher, solange der <<FILEDeskriptor 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 typischerweiseash Derivate wie busybox, dash, BSD sh- zsh, bash, kshund 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 /tmpnicht möglich ist, solange Sie die Datei in den Speicher einfügen können, z.

sed 'H;$!d;x' <file | { read v &&
sed "$script" >file;}

... würde im Allgemeinen zumindest sicherstellen, dass die Datei beim ersten sedProzess vollständig gepuffert wurde , bevor versucht wird, die In / Out-Datei abzuschneiden.

Eine gezieltere und effizientere Lösung könnte sein:

sed '/regex/!H;$!d;x' <file|{ read v && cat >file;}

... weil es nicht stören würde, Zeilen zu puffern, die Sie sowieso löschen wollten.

Ein Test des allgemeinen Falls:

{   nums=/tmp/nums
    seq 1000000 >$nums
    ls -lh "$nums"
    wc -l  "$nums"
    sed 'H;$!d;x' <$nums | { read script &&  ### read always gets a blank
    sed "$script" >$nums;}
    wc -l  "$nums"
    ls -lh "$nums"
}

-rw-r--r-- 1 mikeserv mikeserv 6.6M Dec 22 20:26 /tmp/nums
1000000 /tmp/nums
1000000 /tmp/nums
-rw-r--r-- 1 mikeserv mikeserv 6.6M Dec 22 20:26 /tmp/nums
mikeserv
quelle
Ich gebe zu, dass ich Ihre Antwort vorher nicht im Detail gelesen habe, da sie mit (für mich) nicht praktikablen Lösungen beginnt, die eine Byteanzahl beinhalten (die sich zwischen den vielen Servern unterscheidet) und /tmpsich auf demselben Dateisystem befinden. Ich mag deine Dual- sedVersion. 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.)
Wildcard
2
@ Wildcard - das könnte sein. Sie sollten die Shell jedoch nicht wie eine Datenbank verwenden. die sed| catDas obige Element öffnet niemals die Ausgabe, es sei denn, seddie 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, readist dies nicht erfolgreich, da EOF in der |Pipe gefunden wird, bevor es seine erste neue Zeile liest, und dies daher erst cat >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.
Mikeserv
@Wildcard - wenn ich es wirklich auch in einer Variablen haben wollte, denke ich, würde ich es so machen: Also file=$(sed '/regex/!H;$!d;x' <file | read v && tee file) && cmp - file <<<"$file" || shitewü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.
Mikeserv
@mikeserv: Ich habe jetzt das gleiche Problem wie das OP und finde Ihre Lösung wirklich nützlich. Aber ich verstehe die Verwendung von read scriptund read vin Ihrer Antwort nicht. Wenn Sie mehr darüber erzählen können, werde ich sehr geschätzt, danke!
Sylye
1
@sylye - $scriptist das sedSkript, 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. vist nur ein Platzhalter für eine leere Zeile. In einer bashShell ist dies nicht erforderlich, da bashdie $REPLYShell-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 haben
MikeServ
0

Diese Antwort leiht Ideen aus dieser anderen Antwort und dieser anderen Antwort aus , baut jedoch darauf auf und schafft eine Antwort, die allgemeiner anwendbar ist:

num_bytes = $ (sed '/ myregex / d' / path / to / file / filename | wc -c)
sed '/ myregex / d' / Pfad / zu / Datei / Dateiname 1 <> / Pfad / zu / Datei / Dateiname 
dd if = / dev / null von = / Pfad / zu / Datei / Dateiname bs = "$ num_bytes" suchen = 1

In der ersten Zeile wird der sedBefehl ausgeführt, wobei die Ausgabe in die Standardausgabe (und nicht in eine Datei) geschrieben wird. speziell zu einer Pipe, wcum die Zeichen zu zählen. Die zweite Zeile läuft auch das sedKommando 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 den sed '/myregex/d'Befehl; Für jede Zeile, die es liest, schreibt es genau dieselbe Zeile oder nichts. (Andere Beispiele:s/foo/fu/oder s/foo/bar/wäre sicher, aber s/fu/foo/und s/foo/foobar/würde nicht.)

Beispielsweise:

$ cat filename
It was
a dark and stormy night.
$ sed '/was/d' filename 1<> filename
$ cat filename
a dark and stormy night.
night.

weil diese 32 Datenbytes:

I  t     w  a  s \n  a     d  a  r  k     a  n  d     s  t  o  r  m  y     n  i  g  h  t  . \n

wurde mit diesen 25 Zeichen überschrieben:

a     d  a  r  k     a  n  d     s  t  o  r  m  y     n  i  g  h  t  . \n

Lassen Sie die sieben Bytes night.\nam Ende übrig.

Schließlich ddsucht 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 tun

sed '/ myregex / d' / path / to / file / filename | dd von = / path / to / file / filename conv = notrunc

Beachten Sie außerdem, dass Sie, solange Sie nur Linien entfernen, nur grep -v myregex(wie von Barmar hervorgehoben ) benötigen .

G-Man sagt "Reinstate Monica"
quelle
-3

sed -i 'd' / path / to / file / filename

Chiranjeeb
quelle
1
Hallo! Am besten erklären Sie so detailliert wie relevant, wie Ihre Lösung funktioniert, und beantworten die Frage.
Dhag
2
Dies ist eine schreckliche Nichtantwort. (a) Es wird auf einem vollständigen Dateisystem fehlschlagen, genau wie mein ursprünglicher Befehl; (b) Wenn es erfolgreich wäre, würde es die GANZE Datei leeren und nicht nur die Zeilen, die meinem regulären Ausdruck entsprechen.
Wildcard