`[!]` (Ausrufezeichen in Klammern) Platzhalter in Bash

10

Ich bin auf Globbing-Muster und Wildcards gestoßen und von besonderem Interesse für mich [!].

Dieses Konstrukt ähnelt dem [!]Konstrukt, stimmt jedoch nicht mit Zeichen in den Klammern überein, sondern mit jedem Zeichen, sofern es nicht zwischen [und aufgeführt ist ].

rm myfile [!192]

Ich glaube, dass die oben genannten Dateien alle / alle Dateien entfernen werden, mit Ausnahme aller / aller Dateien, deren Name 192 enthält.

Ich bin jedoch besorgt über die ordnungsgemäße Verwendung mit einer Dateierweiterung und insbesondere über mehrere Bedingungen.

Was wäre die richtige Syntax in einer solchen Situation?

rm myfile [!.gif .csv. mp3] 

oder

rm myfile [!.gif !.csv !.mp3]

Ich mache mir Sorgen, dass der Punkt möglicherweise falsch platziert wird und daher jede Datei mit einem .(was wäre sicherlich eine davon?) Dann manipuliert wird, wenn ich versuchen möchte, bestimmte Dateien zu manipulieren.

Dieses Konstrukt ähnelt dem [ ]Konstrukt, stimmt jedoch nicht mit Zeichen in den Klammern überein, sondern mit jedem Zeichen, sofern es nicht zwischen [und aufgeführt ist ].

(zitiert aus http://www.tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm )

Jetzt; für mich bedeutet das, dass dann ein Singular !ausreicht und alle Werte darin innerhalb des Bereichs enthalten sind.

IronUhlan
quelle
5
Verwenden Sie als allgemeinen Tipp ls my-patternoder echo my-command, um zu überprüfen, ob das Globbing wie gewünscht ausgeführt wird, bevor Sie es rm
ausführen
Denken Sie auch daran, dass viele, wenn nicht die meisten Dateien keine Erweiterungen haben. Erweiterungen auf Linux-Systemen sind normalerweise irrelevant und werden von den meisten Programmen nicht zum Definieren des Dateityps verwendet.
Terdon
2
Beachten Sie, dass Ihr erstes Zitat falsch zitiert wird, jetzt scheint es im Grunde zu sagen, dass [!]es ähnlich, aber nicht ähnlich ist[!]
ilkkachu
2
Es ist erwähnenswert , dass ^die exakt gleiche Bedeutung wie hat !in diesem Zusammenhang daher [^a]schließt agenau wie [!a]tut: Wenn das erste Zeichen nach dem [a ist! oder a ^ dann wird jedes nicht eingeschlossene Zeichen abgeglichen. ( man bash)
Dessert
3
rm myfile [!192]entfernen wird myfilenicht genannt, sowie jede Einzelzeichendatei 1, 9oder 2.
Kevin

Antworten:

16

Zitieren man 7 glob:

An  expression  "[!...]"  matches  a single character, namely any character
that is not matched by the expression obtained by removing the first '!' from it.
(Thus, "[!]a-]" matches any single character except ']', 'a' and '-'.)

Schwerpunkt auf die einzelnen Zeichen Teil hier, []kann nicht für ganze Strings in Bash Pattern Matching verwendet werden.


bashDie Klammererweiterung kann verwendet werden, um Zeichenfolgen abzugleichen, es gibt jedoch keine Möglichkeit (ich weiß), sie zu negieren. Z.B

1.{gif,csv,mp3}

wird erweitert auf

1.gif 1.csv 1.mp3

egal ob diese Dateien existieren.


Wie wchargin hervorhob , shoptkönnen erweiterte Mustervergleichsoperatoren verwendet werden, von denen einer ist !(). Nach dem Laufen shopt -s extglobzB

1.!(gif|csv|mp3)

wird erweitert auf

1.jpg 1.bmp 1.png

wenn diese Dateien im aktuellen Verzeichnis vorhanden sind. Weitere Informationen finden Sie unter (man bash ERWEITERUNG / Erweiterung des Pfadnamens) und Erweiterung des Pfadnamens (Globbing) .


Für das, was Sie versuchen, würde ich immer verwenden find, was die Negation und vieles mehr bietet, z. B. auf folgende Weise:

find . -maxdepth 1 -type f ! -name "*.gif" -a ! -name "*.csv" -a ! -name "*.mp3"

Hier werden nur die Ergebnisse aufgelistet. Wenn Sie sie entfernen möchten, fügen Sie die -deleteOption einfach an das Ende des Befehls an. Wie immer beim Entfernen von Aktionen: Immer zuerst sorgfältig prüfen .

findbietet eine Menge nützlicher Optionen, die sehr gut erklärt werden von man find.

Dessert
quelle
1
Beachten Sie, dass dieser findBefehl rekursiv ist ( bearbeiten: wie ursprünglich gezeigt - ich behalte diesen Kommentar möglicherweise für die zusätzlichen relevanten, aber nicht wesentlichen Informationen, die er übermittelt). Um Dateien zu finden, die sich nur im aktuellen Verzeichnis befinden, aber nicht auch in dessen Unterverzeichnissen - und diese Dateien daher nur zu löschen, wenn die -deleteAktion hinzugefügt wird -, können Sie sie -maxdepth 1unmittelbar danach hinzufügen find .. Dies würde es mit dem Effekt des Globbings in Einklang bringen, da Globs in Bash nicht rekursiv sind, es sei denn, die globstarShell-Option ist aktiviert und **wird verwendet. Der in der Frage gezeigte Glob ist in allen Shells nicht rekursiv.
Eliah Kagan
2
"Es gibt keine Möglichkeit (ich weiß), sie zu negieren" - mit shopt -s extglobkönnen Sie verwenden echo 1.!(gif|csv|mp3), die alles druckt, 1.extwo extnicht gifist csv, oder mp3. (Siehe den Unterabschnitt "Pattern Matching" von man bash.)
wchargin
10

Ich denke, Sie haben die Verwendung von Klammern falsch verstanden. [abc]stimmt mit einem der Zeichen überein a, boder c. [!abc]entspricht einem Zeichen, das nicht ist a , boder c. Also der Befehl

rm myfile [192]

wird entfernen myfileund die Dateien 1, 9und 2weil Sie ein Leerzeichen zwischen myfile und der Klammer haben. Im Gegensatz dazu der Befehl

rm myfile[192]

werden die Dateien entfernen myfile1, myfile9und myfile2.

pLumo
quelle
wahr. Besser zu benutzen find.
pLumo
Eigentlich wird [] für einen Bereich verwendet, [!] Ein einzelnes Zeichen
IronUhlan
1
@IronUhlan, beide nehmen einen Bereich oder eine Liste von Zeichen. Es ist nur so, dass der andere eine Negation ist.
Ilkkachu
7

Klammern [ ]bezeichnen eine Zeichenklasse. Jedes einzelne Zeichen in ihnen kann mit der angegebenen Position übereinstimmen. So

file[192]

entspricht drei möglichen Namen:

file1
file2
file9

Es stimmt nicht überein file192, da es sich nur um das einzelne Zeichen danach handelt file.

Wir können auch Bereiche in Klammern verwenden. [0-9]stimmt mit einer einzelnen Ziffer an der angegebenen Position überein. Für zwei Ziffern brauchen wir [0-9][0-9]. Für eine Ziffer gefolgt von einem Großbuchstaben könnten wir [0-9][A-Z]...

! zeigt Negation an.

file[!192]

stimmt mit Dateien überein, die nach fileAusnahme von 1oder 9oder ein einzelnes Zeichen haben 2. Zum Beispiel wird es mit file7und filesund file%(und vielen anderen) übereinstimmen, aber nicht file192 , weil das zwei zusätzliche Zeichen hat.

Für Ihren Anwendungsfall empfehle ich die Verwendung von findanstelle von [!]. Es hat einen notOperator.

Es ist auch rekursiv , was bedeutet, dass es standardmäßig in alle Unterverzeichnisse verschoben wird. Sie können es auf das aktuelle Verzeichnis mit beschränken -maxdepth 1, wenn Sie dies möchten. Shell-Platzhalter (Globbing) sind nicht rekursiv (obwohl rekursives Globbing mit **aktiviert werden kann).

Angenommen, Sie möchten das Löschen von Symlinks nicht vermeiden, können wir -type ddie negierten Tests ergänzen , um das Löschen von Verzeichnissen zu vermeiden. Vielen Dank an Eliah Kagan für diesen Vorschlag.

find /path -maxdepth 1 -not \( -type d -or -iname "*.gif" -or -iname "*.csv" -or -iname "*.mp3" \)

Wenn Sie sehen, was Sie wollen, können Sie den Befehl mit erneut ausführen -delete

find /path -maxdepth 1  -not \( -type d -or -iname "*.gif" -or -iname "*.csv" -or -iname "*.mp3" \) -delete
Zanna
quelle
2
Und ich denke, Sie können auch mehrere Bereiche gleichzeitig ausführen. Zum Beispiel könnte eine hexadezimale Ziffer sein[0-9a-fA-F]
Wilson
@ Zanna, ja! Ich habe tatsächlich find verwendet :) Ich wusste nicht, dass es einen "nicht" -Operator gibt :)
IronUhlan