Die Shell bietet natürlich auch Befehle zum suchen von Dateien:
find ~ -name datei.txt
Der Befehl find
sucht Dateien nach bestimmten Kriterien. Die Syntax von find
ist dabei: find Suchpfad Kriterien Aktion
. Suchpfad und Kriterien müssen dabei angegeben werden. Der Suchpfad ist der Teilbaum des Verzeichnisbaumes in dem die Datei gesucht wird. Möchte man im aktuellen Arbeitsverzeichnis suchen kann man auch "." als Suchpfad angeben. Möchte man das gesamte System durchsuchen (was dann eine Weile dauern kann) gibt man "/" als Suchpfad an. Im Beispiel ist ~, d. h. das Heimatverzeichnis des jeweiligen Benutzers, als Suchpfad angegeben.
Die Kriterien sind das wonach gesucht wird, beispielsweise der Name einer Datei oder auch andere Kriterien, wie das Veränderungsdatum oder die Gruppenzugehörigkeit oder was auch immer eine Datei an Eigenschaften besitzen kann. Im Beispiel wird nach einem Namen gesucht, daher der Parameter -name
. Dahinter wird der Name angegeben mit datei.txt.
Mit Aktion ist gemeint was mit dem Suchergebnis passieren soll. Sollte keine Aktion angegeben werden, wird als Standardaktion das ausgeben des Suchergebnisses ausgeführt, was im Beispiel der Fall ist. Eine andere Aktion ist -exec
welches einen Befehl erwartet. Mit -exec
kann man das Suchergebnis als Teil eines anderen Befehls nutzen. Z. B.:
find ~ -name "*.bak" -exec rm {} \;
Dieser Befehl sucht im Heimatverzeichnis nach Dateien die mit der Zeichenfolge ".bak" enden. Der * als Wildcardzeichen steht für beliebige Zeichen. Damit der * nicht bereits von der Shell interpretiert wird, sondern erst vom find
Befehl, steht er in Anführungszeichen. Nach dem -exec
muß ein Befehl angegeben werden, in diesem Fall rm
zum löschen. Mit dem Konstrukt {}
wird das jeweilige Suchergebnis eingefügt. Das \;
schließt den Unterbefehl (also die Aktion auf das Suchergebnis) ab.
Hier noch einige andere Beispiele für die Suchparameter von find
:
find /usr -type f -mtime 1
In diesem Fall werden 2 Suchkriterien miteinander kombiniert. Generell kann man so viele Suchkriterien miteinander kombinieren wie man möchte. Diese werden dann alle als logische "und" Verknüpfung angesehen, also müssen dann alle Suchkriterien zutreffen. Mit -type
gibt man den Dateityp der gesuchten Datei als Kürzel an. Dabei bedeutet f file (also normale Datei), d steht für directory (Verzeichnis), l für symbolischen Link, s steht für eine Socket-Datei, p für eine Named Pipe, b für eine Blockgerätedatei und c für eine Zeichengerätedatei. Mit -mtime
wird nach der letzten Veränderungszeit der Datei gesucht. Unix kennt 3 Arten von Zeitstempeln für Dateien: Die mtime (modification time), die ctime (change time) und die atime (access time). Die mtime ist die Zeit zu der der Dateiinhalt zuletzt geändert wurde. Die ctime ist die Zeit zu der zuletzt Metadaten der Datei, wie z. B. der Besitzer oder die Rechte geändert wurden. Die atime ist die Zeit zu der das letzte mal auf die Datei zugegriffen wurde (die atime wird also auch bei reinen Lesezugriffen verändert). Die Zahl hinter dem -mtime
Parameter ist die letzte Veränderungszeit gerundet auf volle Tage, im Beispiel also Dateien die vor einem Tag geändert wurden. Bei Parametern die numerische Werte nutzen wie etwa -mtime
kann man außer einer festen Zahl auch Mindest- oder Maximalwerte angeben. Z. B.:
find /var/log -type f -ctime -2
Das - vor der 2 meint in diesem Fall weniger als 2 Tage. Ein + würde mehr als 2 Tage bedeuten.
find /var/log -cmin -60
Mit diesem Befehl werden alle Dateien unterhalb von /var/log gefunden, deren ctime sich innerhalb der letzten Stunde verändert hat. Im Gegensatz zu -ctime
, steht bei -cmin
die Zahl für Minuten und nicht für Stunden.
find /home -type f -nouser -exec rm {} \;
find /home -type d -nouser -exec rmdir {} \;
Der erste Befehl sucht nach Dateien die keinem User gehören unter /home und löscht die gefundenen Dateien dann. Der zweite Befehl sucht nach Verzeichnissen unter /home die keinem User gehören und löscht diese, falls sie leer sind.
Weitere Möglichkeiten des Befehls find
beschreibt auch die Manpage von find
.
Der Befehl grep
sucht innerhalb von Dateien, nach enthaltenem Text, statt wie find
nach Dateien selbst zu suchen:
grep text datei
Dieser Befehl sucht innerhalb der Datei "datei" nach dem Text "text" und gibt jede Zeile, in der "text" vorkommt aus. Dies kann sehr nützlich sein, bei der Auswertung von Logfiles um diese nach bestimmten Stichwörtern zu durchsuchen.
grep
versteht als Suchaudruck auch sogenannte Reguläre Ausdrücke. Reguläre Ausdrücke sind Suchmuster nach bestimmten Arten von Zeichenketten. Ein Beispiel:
grep "[hH]answurst$" data
Im Beispiel wird in der Datei "data" nach Zeilen gesucht die eine Zeichenkette enthalten die entweder mit einem kleinen h oder einem großen H beginnt und dann mit answurst weitergeht und dann muß die Zeile zu Ende sein. Der Ausdruck [hH]
bezeichnet eine Zeichenklasse. Das Zeichen das an dieser Stelle stehen soll kann ein beliebiges Zeichen, der in der eckigen Klammer vorkommenden Zeichen sein. Möglich sind auch Schreibweisen wie: [A-Z]
, was alle Großbuchstaben sind, oder [0-9]
für alle Ziffern oder auch Kombinationen wie etwa: [A-Za-z0-9]
was alle Großbuchstaben, alle Kleinbuchstaben, alle Ziffern, aber keine Sonderzeichen oder Leerzeichen umfaßt. Das $
am Ende bedeutet das dies das Ende der Zeile sein muß. Das $
hat dabei nur dann diese Sonderbedeutung wenn es am Ende vorkommt. Das umgekehrte ist das Zeichen ^
welches nur am Anfang einer Zeichenkette die Bedeutung hat, das hier die Zeile anfängt. Leere Zeilen lassen sich daher so finden:
grep "^$" data
Dieser Suchausdruck bedeutet das dort wo die Zeile anfängt, sie auch gleich zu Ende sein soll, was dann eben leere Zeilen findet. Die Suchausdrücke von grep
sind hier in Anführungszeichen gesetzt, damit nicht bereits die Shell versucht Sonderzeichen wie etwa [] auszuwerten.
Eine vollständige Einführung in die vielen Möglichkeiten von regulären Ausdrücken würde hier den Rahmen sprengen.
grep
kennt auch noch einige interessante Optionen:
grep -i text datei
Durch den Parameter -i
sage ich grep
das es den Suchausdruck sowohl in Kleinbuchstaben, als auch in Großbuchstaben suchen soll. Normalerweise unterscheidet grep
genau zwischen Groß- und Kleinschreibung.
grep -v text data
Durch den Parameter -v
werden alle Zeilen gefunden die den Suchausdruck nicht enthalten. -v
ist also eine Umkehrung des Suchverhaltens.
Mit den bisher gelernten Befehlen, kann man bereits grundlegende Operationen auf einem Unixsystem durchführen, aber die volle Leistungsfähigkeit der Shell auf einem Unixsystem erreicht man erst durch die Kombination vieler kleiner Befehle.
Befehle lassen sich auf der Shell kombinieren in dem man sogenannte Pipes benutzt. Das Zeichen für eine Pipe ist |
. Durch die Pipe wird die Ausgabe eines Befehls als Eingabe für den darauf folgenden Befehl verstanden.
grep Firefox access.log | wc -l
Dieses Beispiel ist ein sehr praxisnaher Befehl, der die Nützlichkeit dieser Funktion aufzeigt: Zunächst wird durch grep
nach allen Vorkommen der Zeichenkette Firefox in der Datei access.log gesucht. Dabei handelt es sich bei der Datei access.log, z. B. um die Logdatei eines Webservers. Die Ausgabezeilen werden dann an den Befehl wc
übergeben. wc
steht für word count und zählt einfach das was ihm übergeben wird. Das Standardverhalten von wc
ist das zählen von Wörtern, mit dem Parameter -l
jedoch, zählt wc
Zeilen. Der grep
Befehl wiederrum gibt ganze Zeilen aus, auf denen auch noch andere Wörter als nur das Suchwort stehen könnten. Das Ergebnis dieses Befehls ist also die Ausgabe der Anzahl Zeilen im Webserverlog, in denen Firefox vorkommt. Damit hat man bereits ein simples statistisches Auswertungstool für Logfiles, einfach durch die Kombination von 2 Unix Standardbefehlen.
cut
ist ein Befehl mit dem man Spalten aus einer Zeile ausschneiden kann. Kombiniert mit grep
welches ja nach Zeilen sucht, kann man aus Logfiles nur bestimmte Informationen heraus ziehen:
grep "index\.php" access.log | cut -f 1 -d \
Mit diesem Kommando werden aus der Datei access.log zunächst alle Zeilen heraus geholt, die den Text "index.php" enthalten. Der Punkt wurde mit einem \ versehen, da der Punkt als regulärer Ausdruck auch eine Sonderfunktion hat, er steht für ein beliebiges Zeichen. Durch den \ wird dem Punkt seine Sonderfunktion genommen und er steht für sich selbst. Das Ergebnis wird an den Befehl cut
weitergeleitet. Mit dem Parameter -d
wird cut
mitgeteilt, das es als Trennzeichen für Spalten das Leerzeichen verwenden soll. Damit nicht die Shell das Leerzeichen als kein Zeichen ansieht ist es mit einem \ versehen. Im Beispiel ist das Leerzeichen naturgemäß schlecht zu sehen, aber nach dem \ muß ein Leerzeichen folgen. Der Parameter -f
gibt an welche Spalte ausgegeben werden soll, hier also die Erste. Die Erste Spalte in einem Apache Webserverlogfile gibt übrigens die IP-Adresse des Clients aus. Der Befehl gibt also alle IP-Adressen von Clients aus die die Seite index.php aufgerufen haben.
Ein weiterer kleiner oft in Kombinationen angewandter Befehl ist sort
. sort
sortiert Zeilen alphabetisch. uniq
ist ein Befehl der gleiche Zeilen aussortiert. uniq
kann allerdings nur dann gleiche Zeilen aussortieren, wenn diese unmittelbar hintereinander folgen.
grep "index\.php" access.log | cut -f 1 -d \ | sort | uniq
Dieses Kommando erweitert das Beispiel von oben. Die gefundenen IP-Adressen werden zuerst sortiert. Dadurch stehen auch alle gleichen IP-Adressen direkt untereinander. Danach werden gleiche IP-Adressen aussortiert, so das jede IP-Adresse in der Ausgabe nur noch einmal vorkommt. Auf diese Art kann man die Anzahl der Besucher ermitteln die von verschiedenen IP-Adressen gekommen sind.
Generell sollte man sich diese vielen kleinen Unixbefehle, als eine Art Werkzeugkasten vorstellen, aus dem man sich einfach das heraus kombiniert, was man gerade braucht.
Eine Besonderheit des Unix-Dateisystems stellen Links dar. Es gibt 2 Arten von Links: Hardlinks und symbolische Links, die manchmal auch Softlinks genannt werden.
Symbolische Links sind spezielle Dateien die als Zeiger auf eine andere Datei funktionieren. Ruft man einen symbolischen Link in einem Editor auf, wird die Datei geöffnet, auf die der symbolische Link zeigt. Auch wenn man z. B. versucht die Rechte eines symbolischen Links zu ändern, werden die Rechte der Zieldatei geändert. Lediglich beim löschen eines symbolischen Links wird auch nur der symbolische Link und nicht die Zieldatei gelöscht.
Hardlinks sind keine eigenen Dateien, so wie symbolische Links. Hardlinks sind nur ein zusätzlicher Name für eine bestehende Datei im Dateisystem. Ein Hardlink ist genauso wie eine normale Datei und verhält sich auch genau so, mit dem einzigsten Unterschied, das diese Datei unter einem anderen Namen noch mal existiert. Man sollte Hardlinks nicht mit Kopien verwechseln. Bei einer Kopie, nimmt jede Kopie eigenen Speicherplatz ein und verändert man eine von 2 Kopien, wird nur diese eine Kopie und nicht die andere Datei verändert. Bei Hardlinks hingegen, nimmt jeder Hardlink nicht nochmal den selben Speicherplatz weg, sondern der Speicherplatz wird nur ein einziges mal von allen Hardlinks belegt. Ändert man einen Hardlink, ändert sich auch der Inhalt der Originaldatei. 2 Hardlinks sind also eigentlich die selbe Datei. Hardlinks verweisen einfach auf den gleichen Speicherplatz auf der Festplatte, es sind nur 2 unterschiedliche Namen für den gleichen Speicherplatz.
Hardlinks haben 2 Einschränkungen: Zum einen dürfen Hardlinks nicht auf Verzeichnisse gesetzt werden und zum zweiten muß das Ziel eines Hardlinks immer auf der selben Partition liegen. Hardlinks können Partitionsgrenzen logischerweise nicht übersteigen, da es ja nur 2 Namen für den gleichen Speicherort sind. Symbolische Links können hingegen auch über Partitionsgrenzen hinweg angelegt werden und auch auf Verzeichnisse zeigen.
Mit dem Befehl ln
kann man Links anlegen. Benutzt man ln
ohne weitere Parameter, wird ein Hardlink angelegt, benutzt man den Parameter -s
wird ein symbolischer Link angelegt.
ln -s /var/log logs
Dieser Befehl erstellt im aktuellen Verzeichnis den symbolischen Link mit dem Namen logs, der auf das Verzeichnis /var/log zeigt.
Ein ls -l
zeigt den symbolischen Link nun so an:
lrwxrwxrwx 1 rene rene 8 Jun 25 17:06 logs -> /var/log
Das l vor den Rechten zeigt mir das es sich bei dieser Datei um einen symbolischen Link handelt. ls -l
zeigt mir auch das Linkziel (das ich bei einem einfachen ls
nicht sehen würde): Ein Pfeil verweist auf /var/log. Diesen symbolischen Link kann ich nun wie ein ganz normales Verzeichnis nutzen. Gehe ich nun mit cd
nach logs, springe ich in Wirklichkeit nach /var/log.
Ein kleines Experiment zeigt das Anlegen und die Funktionsweise von Hardlinks:
touch datei
ln datei data
Mit dem Befehl touch
lege ich einen neue leere Datei an. Mit dem ln
-Befehl lege ich den zusätzlichen Hardlink data, für die Datei datei an.
vi data
cat datei
Mit dem vi-Editor schreibe ich ein bisschen etwas in die Datei data. Danach lasse ich mir den Inhalt von datei per cat
ausgeben. Ich kann sehen wie der Inhalt den ich in data geschrieben habe, auch in datei enthalten ist.
ls -l
-rw-r--r-- 2 rene rene 11 Jun 25 17:18 data
-rw-r--r-- 2 rene rene 11 Jun 25 17:18 datei
Ein ls -l
zeigt mir die beiden Dateien als ganz normale Dateien an, die beide gleich groß sind und identische Dateieigenschaften besitzen. Die 2 nach den Rechten ist übrigens ein Hardlinkzähler. Diese 2 sagt mir das es von dieser Datei einen zusätzlichen Hardlink gibt.
rm data
ls -l
-rw-r--r-- 1 rene rene 11 Jun 25 17:18 datei
Wenn ich die Datei data lösche, bleibt der Inhalt in der Datei datei dennoch erhalten, der Hardlinkzähler steht jetzt wieder auf 1.
Was passiert eigentlich, wenn das Ziel eines symbolischen Links gelöscht wird? In so einem Fall verwaist der Link, es ist dann ein toter Link der auf keinen Inhalt mehr zeigt.
Unter Windows lassen sich mit dem Zip-Format und einem Packprogramm welches dieses Format beherrscht komprimierte Archive erstellen. Auch Unixsysteme haben diese Möglichkeit, wobei es verschiedene Kompressionsalgorithmen gibt und die Kompression und das Zusammenpacken als Archiv, 2 getrennte Vorgänge sind. Unter Unix haben sich in den letzten Jahren vor allem 2 Kompressionsverfahren durchgesetzt: Gzip und Bzip2. Bereits Gzip besitzt bessere Kompressionsraten als Windows-Zip. Bzip2 komprimiert noch mal stärker als Gzip, benötigt dafür aber auch mehr Zeit und Rechenleistung.
gzip datei
Dieser Befehl komprimiert die Datei datei mit im Gzip-Verfahren. Die Dateiendung .gz wird dabei automatisch angehängt, so das die Datei danach datei.gz heißt.
gunzip datei.gz
Mit diesem Befehl wird die Datei wieder entkomprimiert. Die Endung .gz wird dabei auch wieder automatisch entfernt.
bzip2 datei
Mit dem Befehl bzip2
wird eine Datei mit Bzip2 komprimiert. Auch dabei wird automatisch die Endung .bz2 angehängt.
bunzip2 datei.bz2
Mit diesem Befehl wird die Datei datei.bz2 wieder entkomprimiert. Auch dabei wird die Endung wieder automatisch entfernt.
gzip
und bzip2
komprimieren nur einzelne Dateien. Unix bietet jedoch auch ein Werkzeug um mehrere Dateien, zu einem Dateiarchiv zusammen zu fassen. Dabei werden auch Unixtypische Eigenschaften wie Besitzer und Rechte mit gespeichert. Hierfür existiert der Befehl tar
, der primär für Backups gedacht ist. Tar-Archive lassen sich natürlich um Platz zu sparen auch noch zusätzlich mit gzip
oder bzip2
komprimieren.
tar cf backup.tar daten/
Dieser Befehl packt den Inhalt des Unterverzeichnisses daten/ in das Tar-Archiv backup.tar ein. tar
gehört zu den Befehlen, bei denen man bei Parametern das Minuszeichen auch weglassen kann. Der Parameter c
steht für create, also ein Tar-Archiv erstellen, x
steht für extract also entpacken eines Tar-Archivs, während t für das Anzeigen des Inhaltes eines Tar-Archivs steht. Die f
Option steht für die Angabe des Dateinamens des Tar-Archivs. Das entstandene Archiv ließe sich also mit:
tar xf backup.tar
wieder in das aktuelle Verzeichnis auspacken.
Der Name tar
kommt von Tape Archiver, da tar
ursprünglich als Backupprogramm für Bandlaufwerke entwickelt wurde. Tar-Archive die mit gzip
zusätzlich komprimiert wurden, haben entweder die doppelte Dateiendung .tar.gz oder auch die einfache Endung .tgz. Archive die mit bzip2
komprimiert wurden haben entweder die Endung .tar.bz2 oder .tbz.
Natürlich kann man auch nur einzelne Dateien aus einem Tar-Archiv entpacken:
tar xf backup.tar daten/datei2
Dieser Befehl entpackt aus dem Archiv backup.tar die einzelne Datei daten/datei2, falls diese genau so in dem Archiv existent ist.
tar
speichert auch Dateirechte und Eigentümer. Möchte man all diese Informationen im Originalzustand herstellen, kann man den zusätzlichen Parameter p
verwenden:
tar xpf backup.tar
Mit dem p
Parameter werden alle Dateieigenschaften wie im Original wieder hergestellt. Dies setzt jedoch unter Umständen Root-Rechte vorraus (um Besitzeigenschaften anpassen zu können).
Sollte ich mal in die Situation kommen, ein Windows-Zip Archiv entpacken zu müssen gibt es das Kommando unzip
. unzip
ist allerdings nicht im Standardinstallationsumfang der meisten Systeme enthalten, so das es sein kann, das sie das Programm erst nachinstallieren müssen. Ein unzip paket.zip
entpackt die Zip-Datei paket.zip im aktuellen Verzeichnis.
Cronjobs sind zeitgesteuerte Arbeitsaufträge. Auf Unixartigen Systemen läßt sich in der sogenannten Crontab, also der Tabelle in der alle Cronjobs aufgelistet sind, ein Zeitpunkt und ein Befehl definieren. Der Befehl wird dann zum festgesetzten Zeitpunkt abgearbeitet. Damit lassen sich nächtliche Backups automatisieren oder ständig periodisch anfallende Aufgaben automatisieren. Unter Umständen kann die Berechtigung wer auf einem System alles Cronjobs in Auftrag geben darf, eingegrenzt werden. Crontabs sind Benutzerspezifisch, auf vielen Systemen gibt es aber daneben auch eine systemweite Crontab. Möchte man einen Cronjob in Auftrag geben macht man dies mit dem Befehl crontab
:
crontab -e
Mit dem Parameter -e
wird ein Editor mit der Crontab aufgerufen. Welcher Editor aufgerufen wird, hängt davon ab, welcher Standardeditor eingestellt ist, häufig ist dies vi
, auf manchen Linuxsystemen auch nano
. Die Syntax der Crontab ist folgendermaßen aufgebaut:
Minute Stunde Tag Monat Wochentag Befehl
Über einen Stern (*) kann für eine Zeitspalte eine beliebige Zeit eingetragen werden. Möchte man z. B. einen Befehl jeden Tag um 17:30 Uhr ausführen, sähe der Eintrag so aus:
30 17 * * * Befehl
Da der Befehl jeden Tag ausgeführt werden soll, ist für Tag, Monat und Wochentag jeweils nur ein * eingetragen (also beliebig). Möchte man einen Befehl einmal die Woche jeweils Sonntags um 6:20 Uhr ausführen sieht das so aus:
20 6 * * 7 Befehl
Bei den Wochentagen ist zu beachten, das Crontabs in ihrer Syntax Rücksicht darauf nehmen, das in manchen Gegenden der Welt die Woche am Montag und in anderen Gegenden am Sonntag anfängt. Speziell der Sonntag hat als Zahlenwert daher sowohl die 0, als auch die 7. Beides funktioniert. Der Montag ist dann die 1 usw.
Möchte man einen Befehl am 15. Mai um 16:12 Uhr ausführen sieht das so aus:
12 16 15 5 * Befehl
In diesen Fall setze ich den * nur beim Wochentag ein, da ich meinen Befehl an einem ganz bestimmten Tag ausführen möchte.
Was aber wenn ich einen Befehl alle 10 Minuten ausführen lassen möchte? Auch das geht:
*/10 * * * * Befehl
Per */Intervall kann ich ein Intervall bestimmen. Man kann z. B. auch einen Befehl alle 10 Minuten, aber nur zwischen 8 und 20 Uhr ausführen lassen:
*/10 8-20 * * * Befehl
Es lassen sich also auch Bereiche wie 8-20 Uhr definieren. Auch Aufzählungen in der Art 8,10,12 sind möglich.
Nachdem ich dann meine Cronjobs in der Crontab definiert habe und den Editor wieder verlasse, sind diese Cronjobs aktiv. Die Liste meiner Cronjobs kann ich auch mit folgenden Befehl ausgeben:
crontab -l
Grundsätzlich wird in der Crontab pro Zeile ein Cronjob definiert. Möchte ich alle Cronjobs löschen, kann ich folgenden Befehl benutzen:
crontab -r
Möchte ich nur einzelne Cronjobs löschen, dann lösche ich in der Crontab einfach die betreffende Zeile.
Sollte ein Cronjob irgend welche Ausgaben beim abarbeiten produzieren, dann werden alle Ausgaben per lokaler Mail an den Besitzer der Crontab verschickt, sofern ein lokaler Mailserver auf dem System läuft.