Il existe de nombreuses situations dans lesquelles l’utilisation d’un *
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 voulez exclure un ou deux fichiers ou dossiers de la commande rm
? J'ai cherché 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 {} \;
, comme indiqué ici .
Est-il possible de le faire plus facilement - sans ce détour par find
? Par exemple. rm -rf --exclude='one' --exclude='two' --exclude='three' *
comme dans rsync ou simplement rm -rf -e 'one','two','three' *
?
Peut-être même une possibilité générale d'exclure des éléments de *
(afin que d'autres commandes telles que cp
, mv
, ... n'aient pas à implémenter les leurs)? Quelque chose comme *{'one','two','three'}
ou plus?
Pour Bash, il existe une option Shell appelée extglob
qui est désactivée par défaut pour conserver la compatibilité avec la syntaxe standard de Shell. Extglob complète la syntaxe par des opérateurs supplémentaires tels que !()
, ?()
, @()
et quelques autres.
Pour activer extglob
, tapez shopt -s extglob
. Pour le garder activé pour l'utilisateur actuel, tapez echo 'shopt -s extglob' >> ~/.bashrc
.
Pour l'exemple rm: avec extglob
, vous pouvez utiliser
rm -rf !(one|two|three)
J'ai construit sauf exactement pour cela (mais je n'ai pas encore pu ajouter de documentation).
Je vous ai un dossier comme le suivant:
$ ls
a b c d
Taper except b d
vous donnera a
et c
$ except b d
ac
Vous pouvez maintenant diriger ceci vers rm
.
except b d | xargs -0 rm
Cela peut être difficile à retenir, alors pourquoi ne pas créer un script au-dessus de, appelé rm-except
:
#!/usr/bin/env bash
except "$@" | xargs -0 rm
De même facile est ls-except
:
#!/usr/bin/env bash
except "$@" | xargs -0 ls
Comme alternative à extglob (bien que ce soit une très bonne réponse et que tout le monde devrait avoir shopt -s extglob globstar
dans leur .bashrc), vous pouvez utiliser le variable globale GLOBIGNORE . Supposons que vous souhaitiez obtenir tous les fichiers sauf "foo.txt" et "bar baz.txt":
GLOBIGNORE=foo.txt:'bar baz.txt'
... cependant, cela va activer l'option Shell dotglob
, ce qui signifie que *
correspondra aux fichiers commençant par un point (qui sont normalement masqués). Donc, vous avez besoin de deux commandes:
GLOBIGNORE=foo.txt:'bar baz.txt'
shopt -u dotglob
Puisqu'il s'agit d'une variable globale, chaque glob que vous utilisez sera affecté jusqu'à ce que $ GLOBSTAR ne soit plus défini, soit en vous déconnectant, soit avec
GLOBIGNORE=
De plus, cela ne fonctionnera 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' ./*