web-dev-qa-db-fra.com

Exclure de * en ligne de commande

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?

14
David

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)
33
choroba

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
4
Helper Method

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' ./*
1
evilsoup