Sie riskieren Datenverlust, wenn Sie diesen Linux-Platzhalter falsch verwenden
Mit Linux-Platzhaltern können Sie einen einzelnen Befehl eingeben, der auf ganze Dateigruppen gleichzeitig wirkt. Das ist eine große Zeitersparnis, es sei denn, es geht schief. Und sie können. Destruktiv.
Wozu Platzhalter dienen
Die bekannten Platzhalter sind das Fragezeichen ? und das Sternchen *. Diese können zum Erstellen von Dateinamenmustern verwendet werden. Das Fragezeichen steht für ein beliebiges einzelnes Zeichen und das Sternchen für eine beliebige Zeichenfolge, einschließlich Nullzeichen.
Mit diesem Wissen können wir Muster erstellen, die mit mehreren Dateinamen übereinstimmen. Anstatt alle Dateinamen in die Befehlszeile einzugeben, geben wir stattdessen das Muster ein. Der Befehl wirkt sich auf alle Dateien aus, die mit dem Muster übereinstimmen.
Wenn wir eine Sammlung von Dateien in einem Verzeichnis wie diesem haben:
Wir können Gruppen von Dateien auswählen, die den von uns bereitgestellten Mustern entsprechen.
ls taf_*
Damit erhalten wir alle Dateien mit „taf_“ am Anfang ihres Namens.
ls *.sh
ls s*.sh
Der erste Befehl listet alle Shell-Skriptdateien im Verzeichnis auf. Der zweite Befehl listet nur Dateien auf, die mit „s“ beginnen und gleichzeitig Shell-Skriptdateien sind.
Das scheint alles einfach genug zu sein, und mit ls ist es das auch. Aber auch andere Befehle können von dieser Art des Mustervergleichs Gebrauch machen. Probleme entstehen, wenn die Shell versucht, durch Mustervergleich zu helfen, bevor der Befehl eine Chance bekommt.
Verwenden des Sternchens mit dem Befehl „find“.
Das Erweitern eines Musters in eine Liste übereinstimmender Dateien wird als Globbing bezeichnet.
Es begann als eigenständiger Befehl in Unix Version 6, wurde dann zu einer Bibliothek, die mit anderen Programmen verknüpft werden konnte, und ist heute eine integrierte Shell. Die Erweiterung des Musters wird von der Shell durchgeführt und die Ergebnisse der Erweiterung werden als Befehlszeilenparameter an den Befehl übergeben.
Wir werden uns zwei Beispiele ansehen, die den Befehl „find“ verwenden. Einer tut, was Sie vielleicht erwarten, aber der zweite könnte Sie durchaus überraschen.
Für dieses Beispiel verwenden wir ein Verzeichnis mit einer einzelnen Datei namens readme.txt. Es gibt zwei Verzeichnisse, src und inc. Sie enthalten eine Mischung aus C-, H-, MD- und TMP-Dateien.
ls -R
Wir können find verwenden, um rekursiv Dateien (-type f) mit Namen zu finden, die unserem Muster (-name *.c) entsprechen, und erhalten so eine Liste der C-Dateien.
find . -type f -name *.c
Wir können die Option -not hinzufügen, um die Suche umzukehren und uns alles außer den C-Dateien anzuzeigen.
find . -type f -not -name *.c
Nachdem wir diese Liste überprüft haben, entscheiden wir uns, alles außer den C-Dateien zu löschen. Wir können dies tun, indem wir die Option -delete hinzufügen.
find . -type f -not -name *.c -delete
find .
Der zweite Find-Befehl listet rekursiv alles im und unterhalb des aktuellen Verzeichnisses auf. Übrig bleiben nur noch unsere C-Dateien.
Das hat so funktioniert, wie es die meisten von uns erwartet hätten. Jetzt machen wir genau das Gleiche, aber dieses Mal ist die Datei im aktuellen Verzeichnis keine Textdatei, sondern eine C-Datei.
ls -R
Wir verwenden denselben Suchbefehl und dieselben Suchoptionen, um alles außer den C-Dateien zu löschen. Das ist überhaupt nicht das, was wir wollten.
find . -type f -not -name *.c -delete
find .
Damit wurde jede einzelne Datei im Verzeichnisbaum munter gelöscht, mit Ausnahme der einen C-Datei im aktuellen Verzeichnis.
Wir werden die Dateien noch einmal zurücksetzen und den Befehl so ausgeben, wie wir ihn verwenden sollen.
Alle Dateien sind vorhanden und wir haben wie zuvor eine C-Datei im aktuellen Verzeichnis.
ls -R
Dieses Mal werden wir das Platzhaltermuster in einfache Anführungszeichen setzen.
find . -type f -not -name '*.c' -delete
find .
Das ist es, was wir wollten. Bis auf unsere C-Dateien ist alles verschwunden.
Okay, was ist also schief gelaufen?
Die einfachen Anführungszeichen verhindern, dass die Shell das Dateinamenmuster erweitert. Es wird unverändert an den Befehl oder das Programm übergeben, damit der Befehl darauf reagieren kann.
In dem Beispiel, das funktionierte, hatten wir eine Datei readme.txt im aktuellen Verzeichnis. Die Shell konnte keine Übereinstimmung mit *.c finden und übergab daher *.c, um eine Aktion zu finden.
In dem Beispiel, das alles außer den C-Dateien gelöscht hat, hatten wir eine Datei namens main.c im aktuellen Verzeichnis. Die Shell ordnete das Muster dieser Datei zu und übergab den Namen der Datei an den Befehl „find“. Die Anweisung von find lautete also, alles zu löschen, was nicht main.c hieß.
Wir können dies anhand eines kleinen C-Programms veranschaulichen, das lediglich seine Befehlszeilenparameter im Terminalfenster anzeigt.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int i;
printf("You supplied %d arguments.\n", argc-1);
for (i=1; i<argc; i++)
printf("%-2d) \"%s\"\n", i, argv[i]);
exit (0);
}
Ich habe dies als Datei mit dem Namen glob.c gespeichert und kompiliert mit:
gcc -o glob glob.c
Die Variable argc enthält die Anzahl der Argumente, die wir an das Programm übergeben. Eine for-Schleife durchläuft die Liste der Argumente und gibt jedes einzelne im Terminalfenster aus.
Die for-Schleife beginnt beim Argument eins, nicht bei Null. Es gibt ein Argument Null. Es enthält immer den Namen der Binärdatei selbst. Um das Wasser nicht zu trüben, habe ich es vermieden, es zu drucken. Die einzigen Argumente, die ausgegeben werden, sind diejenigen, die wir in der Befehlszeile bereitstellen.
./glob one two 3 ant beetle cockroach
Versuchen wir das mit *.c als Befehlszeilenparameter.
ls *.c
./glob *.c
Wenn sich keine C-Dateien im aktuellen Verzeichnis befinden, übergibt die Shell *.c an den Befehl „find“. Der Suchbefehl wirkt sich dann auf das Platzhaltermuster selbst aus. Wenn wir jedoch eine C-Datei im aktuellen Verzeichnis haben, übergibt die Shell den Namen der passenden C-Datei an das Programm.
ls *.c
./glob *.c
Unser Programm erhält als Parameter den Namen der C-Datei, das Gleiche gilt auch für den Find-Befehl. Tatsächlich hat find also getan, was ihm gesagt wurde: Alle Dateien außer der Datei main.c löschen.
Dieses Mal werden wir das Platzhaltermuster in einfache Anführungszeichen setzen.
ls *.c
./glob '*.c'
Die Shell ignoriert die Möglichkeit, ihr Globbing auf das Platzhaltermuster anzuwenden, und übergibt es direkt an den Befehl zur weiteren Verarbeitung.
Eine einfache Lösung, Sie können mich zitieren
Als allgemeine Regel gilt, dass Sie Platzhaltermuster in Anführungszeichen setzen, die Sie an Befehle wie „find“ übergeben. Das ist alles, was nötig ist, um ein potenziell katastrophales Unglück dieser Art zu verhindern.