1.4 Advanced Commands
In diesem Lab werden erweiterte Techniken vorgestellt, welche auf den Programmen aufbauen die in den letzten Kapiteln vorgestellt wurden. Dabei wird Wert darauf gelegt, dass auch der nötige Background vermittelt wird um die Commands zu verstehen. Aus diesem Grund ist dieses Lab ein wenig umfangreicher. Hold on to your hats!
Inhalt
find
Im ersten Kapitel haben wir uns die grundlegenden Funktionen von find
angesehen. Natürlich kann find aber noch sehr viel mehr. Nachfolgend schauen
wir uns einige speziellere Anwendungszwecke von find
an.
find nach Zeit
find
kann Suchresultate nach Zeit filtern. Dazu stehen uns verschiedene
Möglichkeiten bereit. Unix Filesysteme schreiben für jede Datei drei
verschiedene Zeitstempel mit. Access Time, Change Time und Modify time.
Wir können uns diese Zeitstempel entweder alle mittels stat $dateiname
ausgeben lassen.
Oder wir verwenden dazu ls
. Hier ein kurzer Überblick über die Bedeutung der
Zeitstempel.
- Access time
ls -lu
- Zeigt die Zeit an zu der die Datei oder der Ordner zuletzt gelesen wurde.
- Wird immer geschrieben, wenn gelesen wird.
- Change time
ls -lc
- Zeigt die Zeit an zu der die Metadaten einer Datei oder eines Ordners zuletzt geändert wurden. Bspw.: Permission oder Ownerchange mittels
chmod
. - Wird auch neu geschrieben, wenn ein Dateiinhalt verändert wird.
- Modify time
ls -l
- Zeigt die Zeit an zu welcher der Inhalt einer Datei zuletzt geändert wurde.
- Verändert sich nicht bei Änderungen der Metadaten (Permissions, Owner, etc).
Da es sich bei diesen Zeitstempeln um Dateiattribute handelt können sie als
Filter für find
eingesetzt werden. Die entsprechenden Optionen heissen
folgendermassen.
-atime n
- Für access time
-ctime n
- Für change time
-mtime n
- Für modify time
Der Parameter n steht hierbei jeweils für eine 24h Einheit. Ein Aufruf von
find
mit dem Argument -mtime +14
liefert uns nur Dateien welche Änderungen
die älter als 2 Wochen sind beinhalten. Das Vorzeichen von n schränkt die Suche
weiter ein. Dabei kann n vorzeichenbehaftet sein.
+n
- Änderungen älter als n Zeiteinheiten
-n
- Änderungen jünger als n Zeiteinheiten
n
- Änderungen genau vor n Zeiteinheiten
|
|
Finden von Dateien die vor mindestens 14 Tagen zuletzt verändert wurden
Suche nach Datum
Stellen wir uns vor einer unserer Server hatte einen Ausfall. Wir wollen die
Ursache eruieren und möchten dazu die Logs eines bestimmten Zeitraums kopieren.
Konkret interessieren uns nur Dateien der letzten zwei Tage, da in unserem
Monitoringsystem zwei Tage vor dem Ausfall eine erhöhte Last festgestellt werden kann.
Dazu können wir die Option -newerXY reference
von find
verwenden. XY steht
hierbei für einen der Modifikatoren at, ct oder mt. Die Referenz kann
entweder eine andere Datei oder ein Textstring sein. Typischerweise verwendet
man dazu date
.
|
|
Finden aller Logs die jünger als zwei Tage sind
Exec all the things!
find
bietet die Möglichkeit auf Matches direkt Aktionen auszuführen. Dazu
verwendet man den Switch -exec
gefolgt von einer bestimmten Syntax.
|
|
Aufruf von find mit exec
[Argumente]
sind die Argumente fürfind
selbst.[Command]
hier steht das gewünschte Kommando welches auf die gefundene Datei angewendet werden soll.{}
ist ein Platzhalter für das Gefundene.[Modifikator]
sagt aus wie die Suchresultate von exec weiterverwendet werden sollen. Bsp:\;
Führe[Command]
für jedes Suchresultat aus.\+
Konkateniere alle Suchresultate und führe[Command]
auf die Liste aus.- Aufpassen! Nicht alle Programme nehmen eine Liste von Befehlen entgegen.
|
|
Lösche alle Logdateien die älter als 2 Tage sind
Verknüpfen verschiedener Suchen
Natürlich lässt find auch logische Verknüpfungen gemäss bool’scher Algebra zu. Wichtig! Werden mehrere Angaben ohne explizite Verknüpfung aneinandergereiht, werden sie implizit AND verknüpft! Die wichtigsten Verknüpfungen sind:
( ... )
Präzedenzmodifikatoren (Hierarchische Ordnung, alles in Klammern wird zusammen ausgeführt)-a, -and
für ein logisches AND-o, -or
für ein logisches OR!, -not
für ein logisches NOT
|
|
Logische Verknüpfungen beim Suchen von Dateien
Die Klammern modifizieren die Präzedenz (Operatorrangfolge) und werden dazu verwendet, logische Aussagen zu bündeln. In obigem Beispiel muss es sich bei dem gesuchten Objekt beispielsweise um eine Datei handeln und sie muss gleichzeitig entweder Root oder Tux gehören. Würden wir die Klammern weglassen, wäre die Aussage eine andere. Dann würde man nach Objekten suchen die dem User Tux gehören oder Objekten und Ordnern welche Root gehören.
xargs
Xargs
ist ein UNIX Werkzeug welches, im Gegensatz zu vielen
anderen Programmen, erlaubt Argumente per stdin
zu übergeben. Aus diesen
Argumenten lässt sich anschliessend ein neuer Programmaufruf bauen.
Aber wozu braucht man das? Nun, viele Werkzeuge wie beispielsweise grep
sind
dazu fähig, Argumente per stdin
anzunehmen. Allerdings gibt es auch Programme
wie mkdir
, rm
oder echo
die dies der Einfachheit halber nicht können.
Kurz gesagt kann man also mittels xargs
den Output eines Programms als Input
für ein Weiteres benutzen.
|
|
Beispiel eines xargs Aufrufes
Oben sehen wir einen Aufruf von xargs
. Per stdin
wird eine Liste von
Wörtern übergeben. Xargs
verwendet diese Liste, um für jedes Wort einen Aufruf
von touch
durchzuführen. Dadurch wird aus jedem übergebenen Wort eine Datei.
Ohne weitere Konfiguration nutzt xargs
das Leerzeichen als Trennzeichen.
Bei Dateinamen die Leerzeichen enthalten ist somit Vorsicht geboten!
Parameter von xargs
|
|
Beispiel von Xargs mit substitution eines Parameters über stdin
Bei obigem Beispiel wird an xargs
über den stdin Kanal ein Input übergeben,
welcher dazu verwendet wird ein beliebiges Command zu vervollständigen. Der
Substitutionsstring ist hier lediglich “%”, es kann sich dabei aber um ein beliebiges
Zeichen oder Wort handeln.
Das Geniale an xargs
ist nun aber dass es sich beim Input auch um eine Liste von
Werten handeln kann, über welche dann entsprechend iteriert wird.
Xargs selbst nimmt einige Parameter entgegen, welche beim Debugging sehr hilfreich sein können.
-I, --replace
- Ersetze das entsprechende Stringargument an jeder Stelle des Commands mit dem Wert der über Stdin an Xargs übergeben wurde.
-t, --verbose
- Generiere für jeden Aufruf eine Ausgabe in der Shell
-p, --interactive
- Lasse jeden Aufruf vor der Ausführung bestätigen
-n, --max-args
- Gibt an wie viele Argumente an einen Aufruf übergeben werden sollen
-0, --null
- Verwende den Nullterminator als Trennzeichen (Ende eines Strings)
Der letzte Punkt bedarf eventuell einer Erläuterung. Greifen wir ein klein
wenig vor und verwenden xargs
kombiniert mit find
um den Inhalt eines
gesuchten Ordners auszugeben.
|
|
Fehlerhafter Aufruf von xargs bei Dateinamen mit Leerzeichen
Oh Graus! Was ist denn da passiert? find
übergibt xargs
einen Dateinamen,
welcher Leerzeichen enthält. Da Leerzeichen von xargs
aber als
Trennzeichen interpretiert werden, wird aus einem Ordner plötzlich ein ganzer
Haufen. Die einzelnen Ordner existieren aber natürlich nicht und können somit auch nicht angezeigt
werden.
Wir können das korrigieren indem wir find
darum bitten, uns das Ende jeder Zeile
mit einem Nullterminator zu dekorieren. Dafür rufen wir xargs
mit der Option -0
auf.
|
|
Richtiger Aufruf von xargs bei Dateinamen mit Leerzeichen
Jetzt sehen wir den Inhalt des gefundenen Ordners. Für den Rest des
Kapitels wird aber der Einfachheit halber auf die Angabe von -print0
verzichtet.
Die Verwendung dieser Syntax ist beim Umgang mit Dateinamen welche Leerzeichen
beinhalten könnten aber zu empfehlen!
|
|
Richtiger Aufruf von xargs bei Dateinamen mit Leerzeichen
Xargs kombiniert mit find
Wie eben bereits kennengelernt ist es sehr beliebt, xargs
im Zusammenhang mit find
zu verwenden.
Möchten wir beispielsweise in einem Ordner alle Dateien entfernen die älter als zwei Wochen sind,
so können wird dies mit einer einfachen Kombination aus find
und xargs
erledigen.
|
|
Verwendung von xargs zur Entfernung alter Dateien
Aber Moment! Wieso sollte man in diesem Beispiel xargs
verwenden?
find
verfügt doch über eine -exec
Funktion, welche es ermöglicht genau dies zu tun?
Ja, das stimmt. Aber xargs
ist bedeutend schneller. Sehen wir uns das anhand eines Beispiels an.
Dazu erzeugen wir in unserem Ordner 1000 leere Textdateien.
|
|
Erstellen von ein paar Dateien
Anschliessend nutzen wird das Programm time
, um die Ausführungszeit beider
Varianten zu messen.
|
|
Vergleich der Ausführungsgeschwindigkeiten
Der Unterschied zwischen den beiden Aufrufen liegt darin, dass find
das
-exec
Kommando auf jedes Suchresultat separat anwendet. Wohingegen xargs
in diesem Beispiel dem Programm rm
eine Liste mit Werten übergibt.
Die zweite Methode ist dadurch natürlich viel effizienter, da rm
nur einmal
ausgeführt wird.
Ganz gewiefte Leser_innen, welche während der Lektüre des erweiterten Kapitels
zu find
nicht geschlafen haben, werden nun einwerfen, dass es mittels find ... \+
ebenfalls möglich ist eine konkatenierte Liste von Befehlen über -exec
an ein
Programm zu übergeben.
Ja, das ist absolut korrekt. Die Ausführungszeit wird sich in dem Beispiel auch
nicht wirklich unterscheiden. Aber natürlich gibt es gute Gründe für die
Verwendung von xargs
:
- Erstens ist die Syntax mit
xargs
viel einfacher. - Zweitens gibt
xargs
den Returncode des ausgeführten Kommandos zurück, find hingegen nur seinen eigenen. - Drittens lassen sich mittels
xargs
auch bequem mehrere Kommandos aneinanderreihen. - Viertens lassen sich die von
xargs
generierten Programmaufrufe auch parallelisieren. Dazu sehen wir uns gleich ein weiteres Beispiel an. - Fünftens wurde vor vielen, vielen Jahren die Unix Philosophie von Männern mit langen, grauen Bärten niedergeschrieben. Sie besagt, dass ein gutes Werkzeug jeweils nur eine einzige Aufgabe erledigen soll, diese dafür so gut wie möglich. Das wollen wir natürlich respektieren. ;)
Xargs parallelisieren
Stellen wir uns vor wir suchen mal wieder die Nadel im Heuhaufen. Irgendwo im Dateisystem befindet sich eine Datei mit einem Inhalt, den wir suchen. Da wir heutzutage viel Geld für Prozessoren mit mehreren Rechenkernen hinblättern, wollen wir diese auch so gut es geht auslasten. Daher möchten wir pro CPU-Core jeweils einen Suchprozess starten.
|
|
Parallelisiertes durchsuchen von Dateien
Sehen wir uns doch kurz an was hier genau passiert.
-print0
und-0
verwenden wir, weil wir nicht wissen ob die Datei Leerzeichen enthält-n1
teiltxargs
mit, pro Ausgabe vonfind
einen Command abzusetzen-P8
teiltxargs
mit, gleichzeitig 8 parallele Prozesse zu starten
Wenn wir uns die Systemauslastung beispielsweise mit htop
anzeigen lassen,
sehen wir dass jeder Kern gleichzeitig am Rechnen ist.
Natürlich lässt sich die Parallelisierung von Prozessen für sehr viel mehr als
nur die Suche nach verlorenen Dateien verwenden. Aber für den Moment belassen
wir es mal dabei.
grep
Wie wir bereits bei find
gesehen haben, sind gewisse Standardwerkzeuge sehr
vielseitig. Das Programm grep
ist hierbei keine Ausnahme. Wie bereits im
Textverarbeitungskapitel erwähnt, basiert grep
auf sogenannten regular
Expressions. Auf eine Beschreibung des kompletten Funktionsumfangs sei an
dieser Stelle aber verzichtet. Stattdessen wollen wir uns die wichtigen
Funktionen ansehen, welche im Alltag oft Verwendung finden.
Grundlagen der regular Expressions
Die Grundlage der regular Expressions basiert darauf, dass man sich Platzhalter
für Gruppen von Zeichen überlegt. Auch spezielle Zeichen wie ein Zeilenumbruch
oder der Anfang einer Zeile können über Regular Expressions als Mittel zur
Suche von Textstellen benutzt werden. Im normalen Modus deckt grep
ohnehin nur die
wichtigsten Funktionen ab. Für kompliziertere Ausdrücke kommt grep -E
respektive egrep
zum Einsatz.
- RegEx Notation
- ^ Markiert den Zeilenanfang
- $ Markiert das Zeilenende
- . Markiert ein beliebiges Zeichen
- ( ) Markiert eine Zeichengruppierung
- { n } Quantifiziert einen vorhergehenden Ausdruck n-mal
- ? Quantifizierung für “höchstens einmal”
- * Quantifizierung für “null oder mehrmals”
- + Quantifizierung für “ein oder mehrmals”
Diesem Lab liegt eine vorbereitete Beispieldatei bei, welche Ausdrücke enthält
anhand deren wir nun einige Funktionalitäten von grep
studieren.
Fangen wir ganz einfach an und schauen uns die Effekte von grep auf unsere
sample.txt
Datei an.
|
|
Inhalt der sample.txt Datei
Case Sensitivity
|
|
Suche eines case insensitiven Strings
Normalerweise ist grep
case sensitiv! Wird grep
aber mit der Option -i
auf eine Textdatei angewendet, so werden alle Instanzen des Suchbegriffes
angezeigt die abgesehen von der Gross/Kleinschreibung übereinstimmen.
Anwendung grundlegender regular Expressions
|
|
Suche mittels grundlegender regular Expressions
Die drei speziellen Zeichen, die wir oben kennengelernt haben verwenden wir nun gleich in der Praxis.
- Im ersten Beispiel werden alle Zeilen angezeigt die mit dem kleinen Wort “puzzle” beginnen.
- Im zweiten Beispiel werden alle Zeilen angezeigt die auf das kleine Wort “pizza” enden.
- Im dritten Beispiel werden alle Zeilen angezeigt die einen beliebigen Buchstaben, gefolgt von “ux” beinhalten.
Inverse matching
Manchmal ist es hilfreich, nicht die Menge an gesuchten Ausdrücken zu spezifizieren, sondern alles was man nicht möchte.
|
|
grep inverse match
Hier werden nun alle Zeilen ausgegeben die nicht das kleine Wort “puzzle” beinhalten.
Anzahl ausgegebener Zeilen modifizieren
Sucht man beispielsweise in einem Konfigurationsfile nach einem gewissen
Parameter, so kommt es oft vor, dass dieser unmittelbar auf der nächsten Zeile
steht. Mittels -A
, -B
und -C
lassen sich die Anzahl angezeigter Zeilen
spezifizieren.
-A, --after-context
-B, --before-context
-C, --context
Konkret verwenden kann man diese Parameter folgendermassen.
|
|
grep inverse match
Anhand des ersten Beispieles sehen wir, dass es nicht so viel Sinn macht nur nach
unserem Suchbegriff zu suchen. Die relevante Information steht nämlich weiter unten.
Durch die Verwendung von -A4
werden auch die 4 nächsten Zeilen nach dem Match
angezeigt. Bei -B4
wären es die 4 Zeilen vor und bei -C4
die 4 Zeilen vor und nach dem Match.
grep -E
Wie bereits angesprochen kann über das Flag -E
auf die erweiterten Funktionen
von grep
zugegriffen werden. Um den Rahmen nicht zu sprengen schauen wir uns
kurz einige sinnvolle Beispiele an.
|
|
Verschiedene Aufrufe von egrep
- Beim ersten Aufruf handelt es sich um die Suche nach der Buchstabengruppe
zz
die mindestens ein oder mehrmals vorkommen darf. - Beim zweiten Aufruf handelt es sich um eine Suche nach der Buchstabengruppe “Linux” oder “Test”.
- Beim dritten Aufruf suchen wir jeden Zeilenanfang der mit einer beliebigen Anzahl Buchstaben des Sets der Kleinbuchstaben
a-z
beginnt.
Verlorene Daten mit grep wiederfinden
Weil grep
so vielseitig ist kann es für unterschiedlichste Zwecke
verwendet werden. Im Folgenden lernen wir einen kreativen Weg kennen wie man
versehentlich gelöschte Daten mittels grep
wiederfinden könnte. Dazu benötigen
wir aber ein wenig Background zum Linuxfilesystem.
Ein wichtiger Teil der Unix Philosophie ist der Grundsatz “Everything is a
file”. Das bedeutet alles, was vom Kernel in den Userspace exportiert wird liegt
irgendwo im Filesystem als einfaches File herum. Beispielsweise wird fast jede
Hardwarekomponente unter dem Devicetree /dev
als Datei repräsentiert.
So zum Beispiel auch die Maus, was wir einfach überprüfen können.
|
|
Ausgabe der Mauskoordinaten aus ästhetischen Gründen hexadezimal dargestellt
Doch wie funktioniert das Ganze? Nun, der entsprechende Treiber kümmert sich im Kernel um das periodische Polling der Mauskoordinaten. Er macht nichts anderes als für jedes HID (Human Input Device) eine Datei zur Verfügung zu stellen und sie laufend zu updaten. Programme welche nun die Mauskoordinaten verwenden möchten, müssen also nichts anderes tun, als aus dieser Datei zu lesen. Diese Philosophie zieht sich vom Framebuffer, über den Netzwerkstack bis hin zu den Blockdevices quer durchs komplette Linuxsystem. Alles ist ein File.
Anbei einige interessante sys-fs Files an welchen bedenkenlos herumgespielt werden kann.
|
|
Experimentieren mit dem sys-fs
Nun da wir wissen, dass alle Geräte irgendwo als Datei vorhanden sind, stellt
sich doch die Frage wie das denn mit den Festplatten aussieht. Tatsächlich
verbirgt sich hinter dem Pfad /dev/sda1
nichts anderes als eine Datei, welche
die erste Partition der ersten Festplatte referenziert. Und Dateien können wir
mit grep
bedenkenlos lesen, respektive durchsuchen. Testen wir das doch an
einem Beispiel.
|
|
Hoppla
In obigem Beispiel wurde versehentlich die Datei mylovelyfile.txt
gelöscht.
Doch was bedeutet denn ein einfaches Löschen für die Datei?
Beim Löschen wird im Filesystem lediglich die Referenz auf die Datei gelöscht.
Dies führt dazu dass der Block in dem die Datei gespeichert war wieder freigegeben
wird. Solange der Block aber nicht überschrieben wurde, kann die Datei noch gelesen werden!
Jedenfalls gilt dies für Filesysteme wie ext2/3/4, xfs, ntfs
. Sogenannte
Copy-On-Write (CoW) Filesysteme wie zfs
oder btrfs
funktionieren anders und
würden eigens Möglichkeiten bieten, eine verlorene Datei wiederherzustellen.
Um eine Datei zu löschen und zu überschreiben kann entweder
shred
oderdd
benutzt werden. Mit diesen Befehlen aber bitte Vorsichtig sein!
Im Ernstfall ist der Computer umgehend herunterzufahren um eine Korruption der
Daten zu verhindern. Die Disk sollte danach über ein Livesystem im
read only Modus eingehängt werden. Anschliessen können wir mit grep
im binary
Modus nach der Datei suchen.
- Vorgehen bei einem Ernstfall
- System umgehend herunterfahren
- System mittels Livesystem (bspw.
grml
oderarch-live
) booten - Festplatte read only mounten
mount -o ro,noload /dev/sda1 /mnt/recovery
- Geeignetes Werkzeug verwenden um die Daten wiederherzustellen
- grep
- testdisk
Da es sich hier aber nur um ein einfaches Beispiel handelt, können wir das
Verfahren auch live ausprobieren. Wir verwenden dazu grep
im binary Modus.
|
|
Binary grep nach verlorenen Dateien
Dabei setzen wir den n
gross genug um die etwa gesamte grösse Datei auszugeben
und suchen nach einem uns bekannten (und möglichst einzigartigen) Textstück in der Datei.
Vor und nach der Datei werden sich entweder Zufällige Daten, oder andere
Dateien befinden. Wenn der Parameter n
zu gross ist, wird der ausgegebene
Bereich überlappen. Das ist grep
aber ziemlich egal.
Falls die Datei noch nicht überschrieben wurde, wird ihr Inhalt abhängig von der
Grösse der Disk nach einigen Minuten auftauchen.
sed
Das Kürzel sed
steht für stream Editor. Wie alle anderen hier vorgestellten
Werkzeuge kann auch sed
eine Vielzahl an Aufgaben übernehmen. Dazu gehören:
- Textsubstitution
- Editieren von Textdateien
- Selektives Ausgeben von Dateien
- Nicht-interaktives Editieren von Text
sed
funktioniert nach dem einfachen Prinzip Einlesen->Verarbeiten->Ausgeben
.
- Einlesen
sed
liest einen input Stream ein (file, pipe, stdin) und speichert ihn temporär im internen Buffer.
- Verarbeiten
- Alle Kommandos und Argumente die
sed
mitgegeben wurden werden auf den Buffer angewendet.
- Alle Kommandos und Argumente die
- Ausgeben
- Ausgeben des gesamten Buffers auf den output Stream.
Dieser Vorgang wiederholt sich bis der gesamte Input abgearbeitet ist. Der
einfachste Aufruf von sed
ist derjenige ohne irgendwelche Argumente.
|
|
Verwendung von sed ohne Argumente auf eine Datei
Wird sed
ohne Argumente aufgerufen, ist der Verarbeitungsschritt trivial und
die Datei wird unverändert ausgegeben. Ein weiterer Kanal mit dem wir sed
mit
Daten versorgen können ist der pipe Operator.
|
|
Verwendung von sed ohne Argumente auf eine Pipe
Ausgeben von Zeilen
Eine der Funktionen von sed
ist das Printkommando.
|
|
Ausprobieren von seds Printkommando
Wie unschwer zu erkennen ist, wird nun im Gegensatz zu vorher jede Zeile
doppelt ausgegeben. Das passiert weil sed
standardmässig bereits jede
eingelesene Zeile ausgibt. Mit dem Printkommando teilt man sed
explizit mit
jede Zeile auszugeben. Daher erscheinen sie nun doppelt. Hier sehen wir auch
schön das gepufferte Verhalten von sed
. Es liest Zeile für Zeile ein, speichert
sie in den Buffer und gibt diesen dann aus. Mit dem Parameter -n
können wir
die Standardausgabe von sed
unterdrücken. Somit wirkt sich nur noch das
Printkommando auf die Ausgabe aus.
|
|
Unterdrückte Standardausgabe
Nun sind wir wieder am gleichen Punkt wie zu Beginn. Doch nun erweitern wir den Printbefehl um die Angabe von sogenannten Adressbereichen oder auch Address Ranges.
Verwendung von Adressbereichen
Mit Adressen ist es möglich, spezifische Bereiche aus einem Stream auszugeben. Man kann dabei einzelne Zeilen oder einen ganzen Bereich von Adressen ausgeben.
|
|
Verwendung von Adressen, Bereichen und Offsets
- Im ersten Beispiel sehen wir die Verwendung einer Adresse. Wir veranlassen
sed
dazu die dritte Zeile auszugeben. - Im zweiten Beispiel verwenden wir den Bereich 1-4, dadurch werden die Zeilen 1-4 ausgegeben.
- Im dritten Beispiel verwenden wir einen Offset. Wir teilen
sed
mit erst bei der zweiten Zeile anzufangen und ab da vier Zeilen auszugeben. - Im vierten Beispiel verwenden wir den Stepoperator mit dem Argument 2. Es werden nur Zeilen ausgegeben die ein ganzzahliges Vielfaches von 2 sind.
- Mit ~2 wird beginnend mit der ersten Zeile jede zweite Zeile ausgegeben
- Mit ~5 jede fünfte
- usw.
Da wir nun die Verwendung von sed
s Ausgabe beherrschen, können wir einen
Schritt weiter gehen und damit beginnen Text zu löschen.
Text löschen
Beim Löschen von Text können wir die genau gleiche Syntax wie beim Ausgeben
verwenden. Wir ersetzen den Printbefehl durch den Deletebefehl. Doch nun benötigen wir die Option -n
nicht mehr, da sed
nur
ausgibt was im Buffer übrig bleibt.
|
|
Löschen mit sed
Verwenden wir den Befehl '1~2d'
, liest sed
die Zeilen in den Buffer ein und
löscht jede Zweite. Alles was übrig bleibt wird ausgegeben. Wir erhalten als
Ausgabe alle Zeilen mit ungerader Nummer.
Wenn wir nun jede zweite Zeile aus einer Datei löschen möchten, weil vielleicht beim Kopieren etwas schief gelaufen ist, könnten wir auf folgende Idee kommen.
|
|
Löschen jeder zweiten Zeile aus einer Datei
Für genau diesen Fall bietet sed
den sogenannten “in-place” Modus an. Wir
können mittels -i
Flag in diesen Modus wechseln. Dadurch werden alle
Änderungen direkt auf die Originaldatei angewendet. Dieser Modus sollte mit
Vorsicht verwendet werden, da sed
keine Fragen stellt sondern einfach
überschreibt oder löscht.
|
|
in-place Löschen jeder zweiten Zeile aus einer Datei
Obiger Aufruf von sed
ist viel einfacher und manipuliert die Originaldatei
direkt. Zur Sicherheit lässt sich aber auch direkt eine Backupdatei erstellen.
Dadurch wird die Originaldatei vor Durchführung der Änderungen mit der Endung “.bak” wegkopiert.
|
|
in-place Löschen jeder zweiten Zeile aus einer Datei mit Erstellen eines Backups
Als kleine Rekapitulation versuchen wir uns an den find
Befehl zu erinnern. Den können wir nämlich
auf zwei verschiedene Arten mit unserer sed
Instruktion verbinden.
|
|
Löschen jeder zweiten Zeile aus allen lokalen Textdateien
Text substituieren
Der wohl am Meisten verwendete Modus von sed
ist der Substitutionsmodus.
Damit wird Text ersetzt. Das ganze basiert auf einer regular Expression, welche
wir ja bereits von grep
kennen.
Die absolut einfachste Form des Suchen und Ersetzens ist ein Ausdruck der Form
's/wasser/bier/'
.
- Mit
s
wechseln wir in den Substitutionsmodus /
fungiert als Trennzeichen (dies kann aber theoretisch auch jedes beliebige andere Zeichen sein. Bspw:','
oder'_'
)- Das erste Wort wird durch das Zweite ersetzt (offensichtlich).
Das Verwenden von verschiedenen Trennzeichen vereinfacht beispielsweise die
Arbeit mit URLs. Sehen wir uns dazu folgendes Beispiel an. Hätten wir '/'
oder '_'
als Trennzeichen verwendet, hätten wir uns wahrscheinlich die Finger verknotet.
|
|
Umbiegen einer URL mittels sed
Damit haben wir auch bereits eine relativ komplizierte Substitution hinter uns.
Daher schauen wir uns die Möglichkeiten noch im Detail an. Dazu verwenden wir
jeweils die zweite sample2.txt
Datei.
|
|
Inhalt der sample2.txt Datei
Fangen wir mit einer einfachen Substitution an. Zur Hervorhebung wurden die
ersetzten Wörter zusätzlich mit *
markiert.
|
|
Einfache Substitution
Mit dem Befehl ersetzen wir das Wort “all” mit “none” auf jeder Zeile.
Doch bereits auf der zweiten Zeile wird klar, dass nicht alle Instanzen
ersetzt wurden! Ohne Erweiterung ersetzt sed
nur den ersten Match. Um pro
Zeile alle Vorkommnisse eines Wortes zu ersetzen, kommt der Modifikator g
zum Einsatz.
|
|
Generelle Substitution
Der Modifikator steuert die Art der Substitution. Wollen wir beispielsweise nur das Zweite “all” ersetzen, gelingt dies folgendermassen.
|
|
Bedingte Substitution
Natürlich sind Modifikatoren kombinierbar! Beispielsweise ist es möglich,
mittels p
nur geänderte Zeilen auszugeben. Dafür müssen wir aber auch wieder
-n
verwenden um die Standardausgabe zu unterdrücken!
|
|
Bedingte substitution mit print statement
Nun wird nur die geänderte Zeile ausgegeben. Es handelt sich um die Zeile, bei
welcher das gesuchte Wort zum 2. Mal aufgetaucht ist.
Möchten wir die Substitution unabhängig von Gross/Kleinschreibung durchführen,
so können wir den Modifikator i
verwenden.
|
|
Case insensitive substitution
Bei sed
gelten die selben Regeln für regular Expression wie bei grep
.
Nachfolgend einige erweiterte Beispiele.
|
|
Substitutionen basierend auf regular Expressions
- Beispiel 1 zeigt die Verwendung des RegEx
^.*with
^
stellt den Zeilenanfang dar.*
eine beliebige Anzahl beliebiger Zeichenwith
lediglich das Wort “with”- Die regular Expression matched vom Zeilenanfang bis zu “with”
- Und replaced in einem zweiten Teil alles durch “none of”
- Beispiel 2 zeigt die Verwendung des selben RegEx aber mit
&
in der Substitution&
ist eine Referenz auf den Match des RegEx- Der Match wird mit Klammern umfasst
- Beispiel 3 zeigt die Verwendung von Gruppen
([a-zA-Z]*)([a-zA-Z]*)
Stellt eine Gruppe aus zwei Wörtern dar welche aus kleinen oder grossen Buchstaben bestehen- Alles innerhalb von runden Klammern wird als Gruppe zusammengefasst
- Klammern müssen “escaped” werden, sonst sucht
sed
auch nach den Klammern
\1 und \2
im Replace sind Referenzen auf die erste und zweite Gruppe- Durch eine gewiefte Verdrehung der Zahlen switchen die Wörter ihre Plätze
- Wir sehen, dass die RegEx nicht perfekt ist da in Zeile 1 ein Komma an zweiter Stelle steht.
- Das lässt sich natürlich durch
sed 's/\([^ ]*\) \([^ ]*\)/\2 \1/' sample2.txt
fixen, aber das wird mir jetzt zu blöd. ;-)
- Das lässt sich natürlich durch
Ab diesem Punkt würde dieses Tutorial zu einem RegEx Tutorial, was den Rahmen
ein wenig sprengen würde. Mit sed
lässt sich eine ganze Reihe von Problemen
lösen. Oft scheitert es aber dann an der Komplexität der Regular Expression.
Hier gilt dasselbe Prinzip wie bei der Salatsauce. Viel hilft viel.