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é :(
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
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/**
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/'
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.