347 votes

Comment utiliser des caractères génériques inverses ou négatifs lors de la recherche de motifs dans un shell unix/linux ?

Disons que je veux copier le contenu d'un répertoire excluant les fichiers et les dossiers dont le nom contient le mot "Musique".

cp [exclude-matches] *Music* /target_directory

Que doit-on mettre à la place de [exclude-matches] pour y parvenir ?

396voto

Vinko Vrsalovic Points 116138

En Bash, vous pouvez le faire en activant la fonction extglob comme ceci (remplacer ls avec cp et ajouter le répertoire cible, bien sûr)

~/foobar> shopt extglob
extglob        off
~/foobar> ls
abar  afoo  bbar  bfoo
~/foobar> ls !(b*)
-bash: !: event not found
~/foobar> shopt -s extglob  # Enables extglob
~/foobar> ls !(b*)
abar  afoo
~/foobar> ls !(a*)
bbar  bfoo
~/foobar> ls !(*foo)
abar  bbar

Vous pouvez par la suite désactiver extglob avec

shopt -u extglob

15 votes

J'aime cette fonctionnalité : ls /dir/*/!(base*)

6 votes

Comment faire pour tout inclure ( ) et excluent également !(b ) ?

5 votes

Comment ferais-tu correspondre, disons, tout ce qui commence avec f sauf foo ?

238voto

tzot Points 32224

Le site extglob L'option de l'interpréteur de commandes vous permet d'obtenir un filtrage plus puissant dans la ligne de commande.

Vous l'activez avec shopt -s extglob et l'éteindre avec shopt -u extglob .

Dans votre exemple, vous feriez initialement :

$ shopt -s extglob
$ cp !(*Music*) /target_directory

L'intégralité de l'offre ext terminé globe terrestre bing opérateurs sont (extrait de man bash ) :

Si l'option extglob de l'interpréteur de commandes est activée à l'aide de l'option intégrée shopt, plusieurs opérateurs étendus de recherche de motifs sont reconnus. sont reconnus. Une liste de motifs est une liste d'un ou plusieurs motifs séparés par un |. Les motifs composites peuvent être formés en utilisant un ou plusieurs des sous-motifs suivants :

  • ?(liste de motifs)
    Correspond à zéro ou une occurrence des motifs donnés
  • *(liste de motifs)
    Correspond à zéro ou plusieurs occurrences des motifs donnés
  • +(liste de motifs)
    Correspond à une ou plusieurs occurrences des motifs donnés
  • @(liste de motifs)
    Correspond à l'un des modèles donnés
  • !(liste de motifs)
    Correspond à tout sauf à l'un des motifs donnés

Donc, par exemple, si vous voulez lister tous les fichiers du répertoire courant qui ne sont pas .c ou .h des fichiers, vous le feriez :

$ ls -d !(*@(.c|.h))

Bien sûr, le globing normal de l'interpréteur de commandes fonctionne, donc le dernier exemple pourrait aussi être écrit comme suit :

$ ls -d !(*.[ch])

1 votes

Quelle est la raison de -d ?

3 votes

@Koveras pour le cas où l'une des .c ou .h files est un répertoire.

0 votes

@DaveKennedy C'est pour lister tout ce qui se trouve dans le répertoire courant. D mais pas le contenu des sous-répertoires qui peuvent être contenus dans le répertoire D .

23voto

ejgottl Points 2178

Pas dans bash (que je sache), mais :

cp `ls | grep -v Music` /target_directory

Je sais que ce n'est pas exactement ce que vous recherchiez, mais cela résoudra votre exemple.

0 votes

Par défaut, ls mettra plusieurs fichiers par ligne, ce qui ne donnera probablement pas les bons résultats.

11 votes

Seulement lorsque stdout est un terminal. Lorsqu'il est utilisé dans un pipeline, ls imprime un nom de fichier par ligne.

0 votes

Ls ne met plusieurs fichiers par ligne que si la sortie se fait dans un terminal. Essayez vous-même - "ls | less" n'aura jamais plusieurs fichiers par ligne.

9voto

Steve Points 317

Si vous voulez éviter le coût en mémoire de l'utilisation de la commande exec, je pense que vous pouvez faire mieux avec xargs. Je pense que ce qui suit est une alternative plus efficace à

find foo -type f ! -name '*Music*' -exec cp {} bar \; # new proc for each exec

find . -maxdepth 1 -name '*Music*' -prune -o -print0 | xargs -0 -i cp {} dest/

4voto

mipadi Points 135410

Vous pouvez également utiliser une méthode assez simple for boucle :

for f in `find . -not -name "*Music*"`
do
    cp $f /target/dir
done

1 votes

Cela fait une recherche récursive, ce qui est un comportement différent de ce que le PO veut.

1 votes

Utiliser -maxdepth 1 pour le non-récursif ?

0 votes

J'ai trouvé que c'était la solution la plus propre sans avoir à activer / désactiver les options du shell. L'option -maxdepth serait recommandée dans ce post pour obtenir le résultat souhaité par le PO, mais tout dépend de ce que vous essayez d'accomplir.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X