Script shell, find -name et extension de caractères generics

J'essaie d'utiliser find -name dans un script sh avec un argument complexe précédemment calculé pour la condition. Simplifié, ça va comme

 cond="-name '*.txt*" find . $cond -ls 

Mais maintenant, j'ai le problème que soit le caractère générique dans $cond est étendu par le shell avant d'appeler find ou non développé par find .

Pour tester cela, dans un directory de test vide j'ai fait:

 touch a.txt b.txt c.dat 

et ensuite itéré

 cond="-name '*.txt'"; echo $cond; find . $cond 

avec des valeurs différentes pour $cond , en utilisant tous les types de citations / échappées auxquelles je pourrais penser.

Soit j'obtiens les quatre inputs (y compris le directory . ) Ou aucun file – ou find plaintes à propos de b.txt étant un primaire ou un opérateur inconnu. Bien sûr, la sortie correcte devrait être juste les deux files text.

Des conseils? Je suis sûr que je manque juste quelque chose de basique.

Dans les shells de type Bourne (excepté zsh ), en laissant une expansion variable non cotée dans le context de list, l'opérateur split + glob . Dans:

 cond="-name '*.txt'"; echo $cond 

Le contenu de $cond est d'abord divisé selon la valeur de la variable spéciale $IFS . Par défaut, c'est sur l'espace ASCII, l'onglet et les caractères de nouvelle ligne.

Donc, c'est divisé en -name et '*.txt' . Ensuite, la partie glob est appliquée. Pour chacun de ces mots contenant des caractères generics (ici * dans le second), le mot est étendu à la list des files qui correspondent à ce model ou laissés intacts si aucun file n'est associé. Ainsi, '*.txt' s'étend à la list des files du directory courant dont le nom commence par ' et se termine par .txt' ou '*.txt' si aucun file ne correspond.

Cette list de mots est ensuite passée en tant qu'arguments séparés à echo . Si aucun file ne correspond, cela signifie que echo recevra 3 arguments: "echo" , "-name" et "'*.txt'" .

Bien sûr, la même chose s'applique à la command find .

Vous voulez que la command find reçoive les arguments -name et *.txt , vous devez donc régler votre opérateur split + glob .

Vous avez besoin de la valeur de $IFS pour correspondre au séparateur que vous utilisez dans $cond . Donc par exemple:

 cond='-name:*.txt' IFS=':' 

Et vous ne voulez pas la partie glob de l'opérateur split + glob , vous devez donc le désactiver avec:

 set -f 

Donc ça devient:

 cond='-name:*.txt' IFS=:; set -f find . $cond -ls 

Ou vous pourriez faire:

 cond='-name *.txt' set -f find . $cond -ls 

et supposons que $IFS n'a pas été modifié par rapport à sa valeur par défaut.

Alternativement, si votre shell prend en charge les arrays, vous pouvez utiliser cela au lieu de countr sur des variables scalaires et attendre que le shell le divise lors de l'expansion.

Cela fonctionnerait avec ksh93 , mksh , bash ou zsh :

 cond=(-name '*.txt') # an array with two elements: "-name" and "*.txt" find . "${cond[@]}" -ls # elements of the array expanded as separate arguments # to find.