In der Befehlszeile von * ausschließen

14

Es gibt viele Situationen, in denen die Verwendung von a *praktisch unvermeidlich ist - z. B. rm -rf *in einem Ordner, der Tausende von Unterordnern und Dateien enthält.

Was aber, wenn Sie nur ein oder zwei Dateien oder Ordner vom rmBefehl ausschließen möchten ? Ich habe mich durchgegoogelt und nur recht komplizierte Lösungen gefunden, wie hierfind . -depth -not \( -name 'one' -o -name 'two' \ -o -name 'three' \) -exec rm {} \; angegeben .

Gibt es eine Möglichkeit, dies auf einfachere Weise zu tun - ohne diesen Umweg find? ZB rm -rf --exclude='one' --exclude='two' --exclude='three' *wie in rsync oder einfach rm -rf -e 'one','two','three' *?

Vielleicht sogar eine allgemeine Möglichkeit auszuschließen , die Dinge aus *(so andere Befehle wie cp, mv... müssen nicht ihre eigenen implementieren)? Sowas *{'one','two','three'}oder so?

David
quelle
3
Wenn Sie nur eine oder zwei Dateien behalten möchten, besteht die einfachste und einfachste Möglichkeit darin, diese Dateien an einen anderen Speicherort zu verschieben, alle verbleibenden Dateien zu löschen und die Dateien zu verschieben, die Sie behalten haben. Wenn Sie viel mehr Dateien behalten möchten, würde ich findmit der --deleteOption (keine Notwendigkeit, rmfür jede Datei auszuführen . Das ist unnötiger Aufwand) verwenden.
Hennes
Das wäre eine Möglichkeit, aber es ist fast so aufwändig wie die, die ich erwähnt habe. Ich weiß, dass ich die Befehle zu so etwas wie kombinieren könnte mv -t /tmp one two three && rm -rf * && mv -t . /tmp/one /tmp/two /tmp/three, aber ich würde eine Lösung vorziehen, die die Möglichkeit bietet, explizit etwas davon auszuschließen *. Es wird sicherlich Situationen geben, in denen das Verschieben oder Kopieren der Dateien zu einem anderen Ziel nicht möglich ist.
David
2
Es ist, weshalb ich es nicht als Antwort gepostet habe. Lediglich als weniger komplexe Vorgehensweise (drei sehr einfache Befehle gegenüber einem etwas komplexeren). Ich hasse Komplexität in Kombination mit rm.
Hennes

Antworten:

33

Für Bash gibt es eine Shell-Option extglob, die standardmäßig deaktiviert ist , um die Kompatibilität mit der Standard-Shell-Syntax zu gewährleisten. Extglob ergänzt die Syntax von zusätzlichen Operatoren wie !(), ?(), @()und einige mehr.

Geben Sie zum Einschalten extglobFolgendes ein shopt -s extglob. Damit es für den aktuellen Benutzertyp eingeschaltet bleibt echo 'shopt -s extglob' >> ~/.bashrc.

Zum rm Beispiel: Mit können extglobSie verwenden

rm -rf !(one|two|three)
Choroba
quelle
Scheint eine gute Lösung zu sein, aber ich möchte sie auch nicht jedes Mal ein- und ausschalten, wenn ich in eine Situation wie die beschriebene gerate.
David
3
@David: du könntest das shoptding in dein stecken ~/.bashrc, es kann nicht schaden.
Enzotib
Wenn ich es richtig verstehe, ändert die Option extglob den Effekt *überhaupt nicht, sondern fügt stattdessen ähnliche Indikatoren (wie die !) hinzu, die zusätzliche Funktionen haben. In diesem Fall wäre es eine schöne Lösung.
David
1
@ David: Genau.
Choroba
1
@David Um die Kompatibilität mit der Standard-Shell-Syntax zu gewährleisten . Es gibt wahrscheinlich andere Situationen, in denen sich das Verhalten ändern kann.
Gerrit
4

Ich habe bis auf genau das gebaut (sry, dass ich noch keine Dokumentation hinzufügen konnte).

Ich habe einen Ordner wie den folgenden:

$ ls
a b c d

Tippen except b dgibt Ihnen aundc

$ except b d
ac

Jetzt können Sie das weiterleiten rm.

except b d | xargs -0 rm

Dies kann schwer zu merken sein. Warum also nicht einfach ein Skript über except erstellen, das heißt rm-except:

#!/usr/bin/env bash

except "$@" | xargs -0 rm

Ähnlich einfach ist ls-except:

#!/usr/bin/env bash

except "$@" | xargs -0 ls
Hilfsmethode
quelle
3
Dies ist nett, aber nicht unbedingt erforderlich, wenn Sie Extglob kennen - askubuntu.com/a/329233/138369
David
1

Als Alternative zu extglob (obwohl das eine sehr gute Antwort ist und jeder shopt -s extglob globstarin seiner .bashrc haben sollte) können Sie die globale Variable $ GLOBIGNORE verwenden . Angenommen, Sie möchten alle Dateien außer 'foo.txt' und 'bar baz.txt' abrufen:

GLOBIGNORE=foo.txt:'bar baz.txt'

... aktiviert jedoch die Shell-Option. Dies dotglobbedeutet, dass *Dateien, die mit einem Punkt beginnen (normalerweise ausgeblendet), abgeglichen werden. Sie benötigen also zwei Befehle:

GLOBIGNORE=foo.txt:'bar baz.txt'
shopt -u dotglob

Da es sich um eine globale Variable handelt, wirkt sich dies auf jedes von Ihnen verwendete Glob aus, bis $ GLOBSTAR entweder durch Abmelden oder mit deaktiviert wird

GLOBIGNORE=

Es funktioniert auch nur mit den Literalzeichenfolgen, die an die Variable übergeben wurden. Sie können sehen, was ich meine, indem Sie $ GLOBIGNORE setzen und den Unterschied zwischen diesen Befehlen betrachten:

printf '%s\n' *
printf '%s\n' ./*
Übelsuppe
quelle