Exclure de * en ligne de commande

14

Il existe de nombreuses situations où l'utilisation d'un fichier *est pratiquement inévitable, par exemple rm -rf *dans un dossier contenant des milliers de sous-dossiers et de fichiers.

Mais que faire si vous souhaitez exclure un ou deux fichiers ou dossiers de la rmcommande? J'ai fait une recherche sur Google et n'ai trouvé que des solutions assez compliquées comme find . -depth -not \( -name 'one' -o -name 'two' \ -o -name 'three' \) -exec rm {} \;indiqué ici .

Y a-t-il une possibilité de le faire plus facilement - sans ce détour find? Par exemple, rm -rf --exclude='one' --exclude='two' --exclude='three' *comme dans rsync ou tout simplement rm -rf -e 'one','two','three' *?

Peut-être même une possibilité générale d'exclure les choses de *(si d' autres commandes telles que cp, mv... ne pas mettre en œuvre leur propre)? Quelque chose comme *{'one','two','three'}ça?

David
la source
3
Si vous ne souhaitez conserver qu'un ou deux fichiers, la manière la plus simple et la plus simple consiste à déplacer ces fichiers vers un autre emplacement, à effacer tous les fichiers restants et à déplacer ceux que vous avez conservés. Si vous souhaitez conserver beaucoup plus de fichiers, je les utiliserais findavec l' --deleteoption (pas besoin d'exécuter rmpour chaque fichier. C'est une surcharge inutile).
Hennes
Ce serait une possibilité, mais elle est presque aussi élaborée que celle que j'ai mentionnée. Je sais que je pourrais combiner les commandes à quelque chose comme mv -t /tmp one two three && rm -rf * && mv -t . /tmp/one /tmp/two /tmp/three, mais je préférerais une solution donnant la possibilité d'exclure explicitement quelque chose de *. Il y aura sûrement des situations où le déplacement ou la copie des fichiers vers une autre destination ne sera pas une option.
David
2
C'est, c'est pourquoi je ne l'ai pas posté comme réponse. Simplement comme une façon moins complexe de faire les choses (trois commandes très simples contre une commandes un peu plus complexes). Je déteste la complexité en combinaison avec rm.
Hennes

Réponses:

33

Pour Bash, il existe une option de shell appelée extglobqui est désactivée par défaut pour conserver la compatibilité avec la syntaxe standard du shell. Extglob complète la syntaxe des opérateurs supplémentaires comme !(), ?(), @()et un peu plus.

Pour allumer extglob, tapez shopt -s extglob. Pour le garder allumé pour le type d'utilisateur actuel echo 'shopt -s extglob' >> ~/.bashrc.

Pour l'exemple rm: Avec, extglobvous pouvez utiliser

rm -rf !(one|two|three)
choroba
la source
Cela semble être une bonne solution, mais je ne veux pas non plus l'activer et le désactiver à chaque fois que je me retrouve dans une situation comme celle décrite.
David
3
@David: vous pourriez mettre la shoptchose dans votre ~/.bashrc, ça ne peut pas faire de mal.
enzotib
Si je comprends bien, l'option extglob ne change pas du tout l'effet *, mais ajoute à la place des indicateurs similaires (comme le !) qui ont des fonctions supplémentaires? Dans ce cas, ce serait une belle solution.
David
1
@David: Exactement.
choroba
1
@David Pour conserver la compatibilité avec la syntaxe standard du shell . Il existe probablement d'autres situations où le comportement peut changer.
gerrit
4

J'ai construit sauf exactement pour ça (désolé que je n'ai pas encore pu ajouter de documentation).

Si vous avez un dossier comme celui-ci:

$ ls
a b c d

La dactylographie except b dvous donnera aetc

$ except b d
ac

Maintenant, vous pouvez diriger cela vers rm.

except b d | xargs -0 rm

Cela peut être difficile à retenir, alors pourquoi ne pas simplement construire un script au-dessus de sauf, appelé rm-except:

#!/usr/bin/env bash

except "$@" | xargs -0 rm

Tout aussi simple est ls-except:

#!/usr/bin/env bash

except "$@" | xargs -0 ls
Méthode d'assistance
la source
3
C'est bien, mais pas vraiment nécessaire quand vous connaissez extglob - askubuntu.com/a/329233/138369
David
1

Comme alternative à extglob (bien que ce soit une très bonne réponse et que tout le monde devrait avoir shopt -s extglob globstardans son .bashrc), vous pouvez utiliser la variable globale $ GLOBIGNORE . Disons que vous voulez obtenir tous les fichiers sauf 'foo.txt' et 'bar baz.txt':

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

... cependant, cela activera l'option shell dotglob, ce qui signifie que *correspondra aux fichiers commençant par un point (qui sont normalement masqués). Vous avez donc réellement besoin de deux commandes:

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

Comme il s'agit d'une variable globale, elle affectera chaque glob que vous utilisez jusqu'à ce que $ GLOBSTAR soit désactivé en vous déconnectant ou avec

GLOBIGNORE=

Il ne fonctionnera également que sur les chaînes littérales passées à la variable. Vous pouvez voir ce que je veux dire en définissant $ GLOBIGNORE et en regardant la différence entre ces commandes:

printf '%s\n' *
printf '%s\n' ./*
evilsoup
la source