web-dev-qa-db-fra.com

Comment passer le caractère générique '*' au paramètre de chemin de la commande find via une variable dans le script?

Je souhaite utiliser find pour rechercher des fichiers dans un ensemble de dossiers restreint par des caractères génériques, mais dans lequel le nom du chemin contient des espaces.

En ligne de commande, c'est facile. Les exemples suivants fonctionnent tous.

find  te*/my\ files/more   -print
find  te*/'my files'/more  -print
find  te*/my' 'files/more  -print

Ceux-ci trouveront des fichiers dans, par exemple, terminal/my files/more et tepid/my files/more.

Cependant, j'ai besoin que cela fasse partie d'un script; ce dont j'ai besoin, c'est quelque chose comme ça:

SEARCH='te*/my\ files/more'
find ${SEARCH} -print

Malheureusement, quoi que je fasse, il semble que je ne sois pas capable de mélanger des caractères génériques et des espaces dans une commande find dans un script. L'exemple ci-dessus renvoie les erreurs suivantes (notez le doublage inattendu de la barre oblique inversée):

find: ‘te*/my\\’: No such file or directory
find: ‘files/more’: No such file or directory

Essayer d'utiliser des guillemets échoue également.

SEARCH="te*/'my files'/more"
find ${SEARCH} -print

Cela renvoie les erreurs suivantes, après avoir ignoré la signification des guillemets:

find: ‘te*/'my’: No such file or directory
find: ‘files'/more’: No such file or directory

Voici un autre exemple.

SEARCH='te*/my files/more'
find ${SEARCH} -print

Comme prévu:

find: ‘te*/my’: No such file or directory
find: ‘files/more’: No such file or directory

Chaque variante que j'ai essayée renvoie une erreur.

J'ai une solution de contournement potentiellement dangereuse car elle renvoie trop de dossiers. Je convertis tous les espaces en un point d'interrogation (caractère générique à caractère unique) comme ceci:

SEARCH='te*/my files/more'
SEARCH=${SEARCH// /?}       # Convert every space to a question mark.
find ${SEARCH} -print

C'est l'équivalent de:

find te*/my?files/more -print

Cela renvoie non seulement les dossiers corrects, mais également terse/myxfiles/more, ce à quoi il n'est pas censé avoir recours.

Comment puis-je atteindre ce que j'essaie de faire? Google ne m'a pas aidé :(

8
Paddy Landau

La même commande devrait fonctionner correctement dans un script:

#!/usr/bin/env bash
find  te*/my\ files/ -print

Si vous avez besoin de comme variable, cela devient un peu plus complexe:

#!/usr/bin/env bash
search='te*/my\ files/'
eval find "$search" -print

ATTENTION:

Utiliser eval comme cela n’est pas sûr et peut entraîner l’exécution de code arbitraire et éventuellement dangereux si vos noms de fichiers peuvent contenir certains caractères. Voir bash FAQ 48 pour plus de détails.

Il vaut mieux passer le chemin comme argument:

#!/usr/bin/env bash
find "$@" -name "file*"

Une autre approche consiste à éviter find et à utiliser les fonctionnalités globbing étendues de bash:

#!/usr/bin/env bash
shopt -s globstar
for file in te*/my\ files/**; do echo "$file"; done

L'option globstar bash vous permet d'utiliser ** pour établir une correspondance récursive:

globstar
      If set, the pattern ** used in a pathname expansion con‐
      text will match all files and zero or  more  directories
      and  subdirectories.  If the pattern is followed by a /,
      only directories and subdirectories match.

Pour le faire agir à 100% comme trouver et inclure des fichiers dot (fichiers cachés), utilisez

#!/usr/bin/env bash
shopt -s globstar
shopt -s dotglob
for file in te*/my\ files/**; do echo "$file"; done

Vous pouvez même echo les directement, sans la boucle:

echo te*/my\ files/**
8
terdon

Qu'en est-il des tableaux?

$ tree Desktop/ Documents/
Desktop/
└── my folder
    └── more
        └── file
Documents/
└── my folder
    ├── folder
    └── more

5 directories, 1 file
$ SEARCH=(D*/my\ folder)
$ find "${SEARCH[@]}" 
Desktop/my folder
Desktop/my folder/more
Desktop/my folder/more/file
Documents/my folder
Documents/my folder/more
Documents/my folder/folder

(*) se développe dans un tableau de tout ce qui correspond au caractère générique. Et "${SEARCH[@]}" se développe dans tous les éléments du tableau ([@]), chacun étant cité individuellement.

Récemment, je me rends compte que se retrouver devrait être capable de cela. Quelque chose comme:

find . -path 'D*/my folder/more/'
2
muru

J'ai finalement trouvé la réponse.

Ajouter une barre oblique inverse à tous les espaces:

SEARCH='te*/my files/more'
SEARCH=${SEARCH// /\\ }

À ce stade, SEARCH contient te*/my\ files/more.

Ensuite, utilisez eval.

eval find ${SEARCH} -print

C'est si simple! Utiliser eval ignore l'interprétation que ${SEARCH} provient d'une variable.

0
Paddy Landau