Implications sécuritaires de l'oubli de la citation d'une variable dans les shell bash / POSIX

Si vous avez suivi unix.stackexchange.com pendant un certain time, vous devriez savoir maintenant que laisser une variable non cotée dans le context de la list (comme dans echo $var ) dans les shells Bourne / POSIX (zsh étant l'exception) signification particulière et ne devrait pas être fait à less que vous ayez une très bonne raison de.

Il est discuté longuement dans un certain nombre de questions et réponses ici (Exemples: Pourquoi mon script shell se bloque-t-il sur les espaces blancs ou d'autres caractères spéciaux?, Quand est-ce que le double-citation est nécessaire?, Expansion d'une variable shell et effet de glob vs expansion de string non citée )

Cela a été le cas depuis la sortie de la shell Bourne à la fin des années 70 et n'a pas été modifiée par le shell Korn (l'un des plus grands regrets de David Korn (question n ° 7) ) ou bash qui copiait principalement le shell Korn c'est ainsi que cela a été spécifié par POSIX / Unix.

Maintenant, nous voyons toujours un certain nombre de réponses ici et même occasionnellement publié le code shell où les variables ne sont pas citées. Vous auriez pensé que les gens auraient appris maintenant.

D'après mon expérience, il y a principalement 3 types de personnes qui omettent de citer leurs variables:

  • débutants. Ceux-ci peuvent être excusés comme il est vrai que c'est une syntaxe complètement inintuitive. Et c'est notre rôle sur ce site pour les éduquer.

  • gens oublieux.

  • des gens qui ne sont pas convaincus, même après des coups de marteau répétés, qui pensent que l'auteur du Bourne n'a certainement pas voulu nous citer toutes nos variables .

Peut-être pouvons-nous les convaincre si nous exposons le risque associé à ce type de comportement.

Quelle est la pire chose qui puisse arriver si vous oubliez de citer vos variables? Est-ce vraiment si mauvais?

De quel genre de vulnérabilité parle-t-on ici?

Dans quels contexts peut-il y avoir un problème?

Préambule

Premièrement, je dirais que ce n'est pas la bonne façon de régler le problème. C'est un peu comme dire " tu ne devrais pas tuer les gens parce que sinon tu iras en prison ".

De même, vous ne citez pas votre variable, car vous introduisez des vulnérabilités de security. Vous citez vos variables parce que c'est faux de ne pas (mais si la peur de la prison peut aider, pourquoi pas).

Un petit résumé pour ceux qui viennent de sauter dans le train.

Dans la plupart des shells, en laissant une extension de variable non citée (bien que cela (et le rest de cette réponse) s'applique également à la substitution de command ( `...` ou $(...) ) et à l'expansion arithmétique $((...)) ou $[...] )) a une signification très particulière. La meilleure façon de le décrire est que c'est comme invoquer une sorte d'opérateur implicite split + glob .

 cmd $var 

dans une autre langue serait écrit quelque chose comme:

 cmd(glob(split($var))) 

$var est d'abord divisé en une list de mots selon des règles complexes impliquant le paramètre spécial $IFS (la partie divisée ), puis chaque mot résultant de cette division est considéré comme un model qui est étendu à une list de files qui le correspondent la partie glob ).

Par exemple, si $var contient *.txt,/var/*.xml et $IFS contient , cmd sera appelé avec un certain nombre d'arguments, le premier étant cmd et les suivants étant les files txt dans le courant directory et les files xml dans /var .

Si vous vouliez appeler cmd avec seulement les deux arguments littéraux cmd et *.txt,/var/*.xml , écrivez:

 cmd "$var" 

qui serait dans votre autre langue plus familière:

 cmd($var) 

Qu'entendons-nous par vulnérabilité dans un shell ?

Après tout, on sait depuis la nuit des time que les scripts shell ne doivent pas être utilisés dans des contexts sensibles à la security. Certes, OK, laisser une variable non citée est un bug mais ça ne peut pas faire beaucoup de mal, n'est-ce pas?

Bien que personne ne vous dise que les scripts shell ne devraient jamais être utilisés pour les CGI web, ou que heureusement la plupart des systèmes n'autorisent pas les scripts shell setuid / setgid de nos jours, une chose que shellshock (le bug bash exploitable à distance qui headlines en septembre 2014) a révélé que les shells sont encore largement utilisés là où ils ne le devraient probablement pas: dans les CGI, dans les scripts hooks client DHCP, dans les commands sudoers, invoqués par des commands setuid (sinon)

Parfois inconsciemment. Par exemple, le system("cmd $PATH_INFO") dans un script CGI php / perl / python invoque un shell (sans mentionner le fait que cmd lui-même peut être un script shell et son auteur peut ne jamais s'attendre à être appelé depuis un CGI).

Vous avez une vulnérabilité quand il y a un path pour l'escalade des privilèges, c'est-à-dire quand quelqu'un (appelons-le l'attaquant ) est capable de faire quelque chose auquel il n'est pas destiné.

Invariablement, cela signifie que l'attaquant fournit des données, ces données étant traitées par un user / process privilégié qui, par inadvertance, fait quelque chose qu'il ne devrait pas faire, dans la plupart des cas à cause d'un bogue.

Fondamentalement, vous avez un problème lorsque votre code bogué traite des données sous le contrôle de l'attaquant .

Maintenant, ce n'est pas toujours évident d'où viennent ces données , et il est souvent difficile de savoir si votre code traitera des données non fiables.

En ce qui concerne les variables, dans le cas d'un script CGI, il est évident que datatables sont les parameters CGI GET / POST et des choses comme les cookies, le path, l'hôte … les parameters.

Pour un script setuid (exécuté en tant qu'user lorsqu'il est appelé par un autre), ce sont les arguments ou les variables d'environnement.

Un autre vector très commun est le nom de file. Si vous obtenez une list de files à partir d'un directory, il est possible que des files aient été plantés par l'attaquant .

À cet égard, même à l'invite d'un shell interactif, vous pourriez être vulnérable (lors du traitement de files dans /tmp ou ~/tmp par exemple).

Même un ~/.bashrc peut être vulnérable (par exemple, bash l'interprétera lorsqu'il sera invoqué sur ssh pour exécuter un ForcedCommand comme dans les deployments de servers git avec certaines variables sous le contrôle du client).

Désormais, un script peut ne pas être appelé directement pour traiter des données non fiables, mais il peut être appelé par une autre command qui le fait. Ou votre code incorrect peut être copié dans des scripts qui le font (par vous 3 ans en bas de la ligne ou un de vos collègues). Un endroit où il est particulièrement critique est dans les réponses dans les sites de questions et réponses car vous ne saurez jamais où des copys de votre code peuvent finir.

À l'affaire; A quel point est-ce mauvais?

Laisser une variable (ou substitution de command) non-citée est de loin la source numéro un des failles de security associées au code shell. En partie parce que ces bugs se traduisent souvent par des vulnérabilités mais aussi parce qu'il est si courant de voir des variables non-cotées.

En fait, lorsque vous searchz des vulnérabilités dans le code shell, la première chose à faire est de searchr des variables non-cotées. Il est facile de repérer, souvent un bon candidat, généralement facile à repérer datatables contrôlées par l'attaquant.

Il existe un nombre infini de façons dont une variable non citée peut devenir une vulnérabilité. Je vais juste donner quelques tendances communes ici.

Divulgation d'information

La plupart des gens se heurtent à des bogues associés à des variables non-cotées en raison de la partie fractionnée (par exemple, les files ont souvent des espaces dans leurs noms et la valeur par défaut est IFS). Beaucoup de gens vont négliger la partie glob . La partie glob est au less aussi dangereuse que la partie divisée .

Un globage effectué sur une input externe non analysée signifie que l'attaquant peut vous faire lire le contenu de n'importe quel directory.

Dans:

 echo You entered: $unsanitised_external_input 

si $unsanitised_external_input contient /* , cela signifie que l'attaquant peut voir le contenu de / . Pas de grosse affaire. Il devient plus intéressant avec /home/* qui vous donne une list des noms d'user sur la machine, /tmp/* , /home/*/.forward pour des conseils sur d'autres pratiques dangereuses, /etc/rc*/* pour activé services … Pas besoin de les nommer individuellement. Une valeur de /* /*/* /*/*/*... listra tout le système de files.

Vulnérabilités de déni de service.

En prenant le cas précédent un peu trop loin et nous avons un DoS.

En fait, toute variable non cotée dans le context de list avec une input non-analysée est au less une vulnérabilité de déni de service.

Même les scripteurs de shell experts oublient souvent de citer des choses comme:

 #! /bin/sh - : ${QUERYSTRING=$1} 

: est la command no-op. Qu'est ce qui pourrait aller mal?

Cela signifie affecter $1 à $QUERYSTRING si $QUERYSTRING était $QUERYSTRING . C'est un moyen rapide de rendre un script CGI appelable à partir de la command line.

Ce $QUERYSTRING est encore développé et comme il n'est pas coté, l'opérateur split + glob est invoqué.

Maintenant, il y a des globs qui sont particulièrement chers à développer. Le /*/*/*/* est assez mauvais car cela signifie listr les directorys jusqu'à 4 niveaux plus bas. En plus de l'activité du disque et du processeur, cela signifie stocker des dizaines de milliers de paths de files (40k ici sur un server minimal VM, 10k de quels directorys).

Maintenant /*/*/*/*/../../../../*/*/*/* signifie /*/*/*/*/../../../../*/*/*/* x 10k et /*/*/*/*/../../../../*/*/*/*/../../../../*/*/*/* est suffisant pour amener même la machine la plus puissante à genoux.

Essayez-le vous-même (bien que vous soyez prêt à vous planter ou à vous suspendre):

 a='/*/*/*/*/../../../../*/*/*/*/../../../../*/*/*/*' sh -c ': ${a=foo}' 

Bien sûr, si le code est:

 echo $QUERYSTRING > /some/file 

Ensuite, vous pouvez remplir le disque.

Il suffit de faire une search google sur shell cgi ou bash cgi ou ksh cgi , et vous findez quelques pages qui vous montrent comment écrire des CGI dans des coquilles. Remarquez que la moitié de ceux qui traitent les parameters sont vulnérables.

Même celui de David Korn est vulnérable (regardez la manipulation des cookies).

jusqu'à des vulnérabilités arbitraires d'exécution de code

L'exécution de code arbitraire est le pire type de vulnérabilité, car si l'attaquant peut exécuter n'importe quelle command, il n'y a pas de limite à ce qu'il peut faire.

C'est généralement la partie divisée qui mène à ceux-ci. Cette division résulte en plusieurs arguments à transmettre aux commands lorsque seulement un est attendu. Alors que le premier de ceux-ci sera utilisé dans le context attendu, les autres seront dans un context différent donc potentiellement interprété différemment. Mieux avec un exemple:

 awk -v foo=$external_input '$2 == foo' 

Ici, l'intention était d'assigner le contenu de la variable shell $external_input à la variable foo awk .

À présent:

 $ external_input='x BEGIN{system("uname")}' $ awk -v foo=$external_input '$2 == foo' Linux 

Le deuxième mot résultant de la division de $external_input n'est pas assigné à foo mais considéré comme awk code (ici qui exécute une command arbitraire: uname ).

C'est surtout un problème pour les commands qui peuvent exécuter d'autres commands ( awk , env , sed (GNU one), perl , find …) surtout avec les variantes GNU (qui acceptent les options après les arguments). Parfois, vous ne soupçonnez pas que des commands puissent être exécutées par d'autres, comme ksh [ s, zsh / ksh 's printf

 for file in *; do [ -f $file ] || continue something-that-would-be-dangerous-if-$file-were-a-directory done 

Si nous créons un directory appelé x -o yes , le test devient positif, car c'est une expression conditionnelle complètement différente que nous évaluons.

Pire, si nous créons un file appelé x -oa[0$(uname>&2)] -gt 1 , toutes les implémentations ksh au less (qui inclut le sh de la plupart des Unices commerciaux et de certains BSD) effectuer une évaluation arithmétique sur les opérateurs de comparaison numérique de la command [ .

 $ touch 'x -oa[0$(uname>&2)] -gt 1' $ ksh -c 'for f in *; do [ -f $f ]; done' Linux 

Bien sûr, s'il ne peut pas get d'exécution arbitraire, l'attaquant peut se contenter de less de dégâts (ce qui peut aider à get une exécution arbitraire). Toute command qui peut écrire des files ou modifier les permissions, la propriété ou avoir un effet secondaire ou principal pourrait être exploitée.

Toutes sortes de choses peuvent être faites avec des noms de files.

 $ touch -- '-R ..' $ for file in *; do [ -f "$file" ] && chmod +w $file; done 

Et vous finissez par faire .. writeable (récursivement avec GNU chmod ).

Les scripts qui effectuent le traitement automatique des files dans des zones accessibles au public comme /tmp doivent être écrits avec beaucoup de soin.

Qu'en est-il de [ $# -gt 1 ]

C'est quelque chose que je trouve exaspérant. Certaines personnes se donnent la peine de se requestr si une expansion particulière peut être problématique pour décider si elles peuvent omettre les citations.

C'est comme dire. Hé, on dirait que $# ne peut pas être soumis à l'opérateur split + glob, demandons au shell de split + glob . Ou Hey, écrivons un code incorrect juste parce que le bug est peu susceptible d'être touché .

Maintenant, comment est-il improbable? OK, $# (ou $! $? Ou toute substitution arithmétique) ne peut contenir que des numbers (ou - pour certains) de sorte que la partie glob soit sortie. Pour que la partie fractionnée fasse quelque chose, il suffit que $IFS contienne des numbers.

Avec quelques shells, $IFS peut être hérité de l'environnement, mais si l'environnement n'est pas sûr, c'est fini de toute façon.

Maintenant, si vous écrivez une fonction comme:

 my_function() { [ $# -eq 2 ] || return ... } 

Cela signifie que le comportement de votre fonction dépend du context dans lequel il est appelé. Ou en d'autres termes, $IFS devient l'une des inputs à lui. Ssortingctement parlant, lorsque vous écrivez la documentation de l'API pour votre fonction, il devrait être quelque chose comme:

 # my_function # inputs: # $1: source directory # $2: destination directory # $IFS: used to split $#, expected not to contain digits... 

Et le code d'appel de votre fonction doit vous assurer que $IFS ne contient pas de numbers. Tout ça parce que vous n'aviez pas envie de taper ces deux guillemets doubles.

Maintenant, pour que ce bug [ $# -eq 2 ] devienne une vulnérabilité, vous auriez besoin d'une certaine façon pour que la valeur de $IFS devienne sous le contrôle de l'attaquant . Concevable, cela ne se produirait pas normalement à less que l'attaquant n'ait réussi à exploiter un autre bug.

Ce n'est pas du tout inconnu. Un cas courant est celui où les gens oublient d'assainir datatables avant de les utiliser en expression arithmétique. Nous avons déjà vu ci-dessus qu'il peut permettre l'exécution de code arbitraire dans certains shells, mais dans tous, il permet à l'attaquant de donner à n'importe quelle variable une valeur entière.

Par exemple:

 n=$(($1 + 1)) if [ $# -gt 2 ]; then echo >&2 "Too many arguments" exit 1 fi 

Et avec un $1 avec la valeur (IFS=-1234567890) , cette évaluation arithmétique a l'effet secondaire des parameters IFS et la command [ suivante échoue, ce qui signifie que la vérification de trop d'arguments est contournée.

Qu'en est-il lorsque l'opérateur split + glob n'est pas invoqué?

Il y a un autre cas où des citations sont nécessaires autour des variables et d'autres extensions: quand il est utilisé comme un model.

 [[ $a = $b ]] # a `ksh` construct also supported by `bash` case $a in ($b) ...; esac 

ne testez pas si $a et $b sont les mêmes (sauf avec zsh ) mais si $a correspond au model dans $b . Et vous devez citer $b si vous voulez comparer en tant que strings (même chose dans "${a#$b}" ou "${a%$b}" ou "${a##*$b*}"$b devrait être cité s'il ne doit pas être pris comme model).

Cela signifie que [[ $a = $b ]] peut returnner true dans les cas où $a est différent de $b (par exemple quand $a est anything et $b est * ) ou peut returnner false quand ils sont identiques par exemple lorsque les deux $a et $b sont [a] ).

Cela peut-il constituer une vulnérabilité de security? Oui, comme n'importe quel bug. Ici, l'attaquant peut modifier le stream de code logique de votre script et / ou casser les hypothèses que votre script est en train de faire. Par exemple, avec un code comme:

 if [[ $1 = $2 ]]; then echo >&2 '$1 and $2 cannot be the same or damage will incur' exit 1 fi 

L'attaquant peut contourner le contrôle en passant '[a]' '[a]' .

Maintenant, si ni ce model ni l'opérateur split + glob ne s'appliquent, quel est le danger de laisser une variable non-citée?

Je dois admettre que j'écris:

 a=$b case $a in... 

Là, citer n'est pas nocif mais n'est pas ssortingctement nécessaire.

Cependant, l'un des effets secondaires de l'omission des citations dans ces cas (par exemple dans les réponses aux questions et réponses) est qu'il peut envoyer un message erroné aux débutants: il peut être tout à fait normal de ne pas citer de variables .

Par exemple, ils peuvent commencer à penser que si a=$b est OK, export a=$b serait aussi bien (ce qui n'est pas dans de nombreux shells comme dans les arguments de la command export ) env a=$b .

Qu'en est-il de zsh ?

zsh a résolu la plupart de ces maladresses de design. En zsh (au less quand vous n'êtes pas en mode d'émulation sh / ksh), si vous voulez splitter , ou globuler , ou une correspondance de model , vous devez le requestr explicitement: $=var à split et $~var à glob ou pour le contenu de la variable à traiter en tant que model.

Cependant, le fractionnement (mais pas le regroupement) se fait toujours implicitement sur la substitution de command non citée (comme dans echo $(cmd) ).

En outre, un effet secondaire parfois indésirable de ne pas citer de variable est la suppression des vides . Le comportement de zsh est similaire à ce que vous pouvez réaliser dans d'autres shells en désactivant complètement la fonction glob (avec set -f ) et splitting (avec IFS='' ). Toujours dedans:

 cmd $var 

Il n'y aura pas de split + glob , mais si $var est vide, au lieu de recevoir un argument vide, cmd ne recevra aucun argument du tout.

Cela peut causer des bogues (comme l'évident [ -n $var ] ). Cela peut briser les attentes et les hypothèses d'un script et causer des vulnérabilités, mais je ne peux pas find un exemple pas trop tiré par les cheveux à l'instant).

Qu'en est-il quand vous avez besoin de l'opérateur split + glob ?

Oui, c'est typiquement quand vous voulez laisser votre variable non cotée. Mais alors vous devez vous assurer que vous accordez correctement vos opérateurs split et glob avant de l'utiliser. Si vous ne voulez que la partie scindée et non la partie glob (ce qui est le cas la plupart du time), vous devez désactiver globbing ( set -o noglob / set -f ) et réparer $IFS . Sinon, vous provoquerez aussi des vulnérabilités (comme l'exemple CGI de David Korn mentionné ci-dessus).

Conclusion

En bref, laisser une variable (ou une substitution de command ou une expansion arithmétique) non cotée dans des coquilles peut être très dangereux, en particulier lorsqu'il est fait dans de mauvais contexts, et il est très difficile de savoir quels sont ces contexts erronés.

C'est l'une des raisons pour lesquelles il est considéré comme une mauvaise pratique .

Merci d'avoir lu jusqu'à maintenant. Si cela vous dépasse, ne vous inquiétez pas. On ne peut pas s'attendre à ce que tout le monde comprenne toutes les implications de l'écriture de leur code comme ils l'écrivent. C'est pourquoi nous avons des recommandations de bonnes pratiques , afin qu'elles puissent être suivies sans nécessairement comprendre pourquoi.

(et dans le cas où cela n'est pas encore évident, évitez d'écrire du code sensible à la security dans des coquilles).

Et veuillez citer vos variables sur vos réponses sur ce site!

[Inspiré par cette réponse par cas .]

Mais que faire si …?

Mais que faire si mon script définit une variable à une valeur connue avant de l'utiliser? En particulier, que se passe-t-il si une variable est définie sur l'une des deux valeurs possibles ou plus (mais elle l'affecte toujours à quelque chose de connu) et qu'aucune des valeurs ne contient de caractères espace ou glob? N'est-il pas sûr de l'utiliser sans citations dans ce cas ?

Et que se passe-t-il si l'une des valeurs possibles est la string vide, et je dépend de "suppression des vides"? C'est-à-dire, si la variable contient la string vide, je ne veux pas get la string vide dans ma command; Je veux rien get. Par exemple,

  si some_condition
 puis
     ignorecase = "- i"
 autre
     ignorecase = ""
 Fi
                                         # Notez que les citations des commands ci-dessus ne sont pas ssortingctement nécessaires.
 grep $ ignorecase other_ grep _args 

Je ne peux pas dire grep "$ignorecase" other_ grep _args ; cela échouera si $ignorecase est la string vide.

Réponse:

Comme indiqué dans l'autre réponse, cela échouera toujours si IFS contient un - ou un i . Si vous avez veillé à ce que IFS ne contienne aucun caractère dans votre variable (et vous êtes sûr que votre variable ne contient pas de caractères glob), cela est probablement sans danger.

Mais il y a un moyen plus sûr (bien qu'il soit un peu laid et peu intuitif): utilisez ${ignorecase:+"$ignorecase"} . A partir de la spécification POSIX Shell Command Language , sous 2.6.2 Expansion des parameters ,

${ parameter :+[ word ]}

    Utilisez une autre valeur. Si le parameter est désactivé ou nul, null doit être remplacé; sinon, l'extension du word (ou une string vide si le word est omis) doit être remplacée.

L'astuce ici, telle qu'elle est, est que nous utilisons ignorecase comme parameter et "$ignorecase" comme word . Donc ${ignorecase:+"$ignorecase"} signifie

Si $ignorecase est non $ignorecase ou nul (c.-à-d $ignorecase ), le caractère nul (c.-à-d., $ignorecase ) doit être remplacé; Sinon, l'extension de "$ignorecase" sera remplacée.

Cela nous amène là où nous voulons aller: si la variable est définie sur la string vide, elle sera "enlevée" (toute cette expression convolutée ne sera évaluée à rien – pas même une string vide), et si la variable a un non -vide valeur, nous obtenons cette valeur, cité.


Mais que faire si …?

Mais que faire si j'ai une variable que je veux / besoin d'être divisé en mots? (Ceci est par ailleurs comme le premier cas, mon script a mis la variable, et je suis sûr qu'il ne contient pas de caractères glob, mais il peut contenir des espace (s), et je veux qu'il soit divisé en arguments séparés à l'espace limites.
PS Je veux encore enlever les vides.)

Par exemple,

  si some_condition
 puis
     criteria = "- type f"
 autre
     critères = ""
 Fi
 si some_other_condition
 puis
     criteria = "$ criteria -mtime +42"
 Fi
 find "$ start_directory" $ criteria other_ find _args 

Réponse:

Vous pourriez penser que c'est un cas pour utiliser eval . Non! Résistez à la tentation de penser même à utiliser eval ici.

Encore une fois, si vous avez veillé à ce que IFS ne contienne aucun caractère dans votre variable (à l'exception des espaces que vous souhaitez honorer) et que vous êtes sûr que votre variable ne contient pas de caractères glob, probablement sans danger.

Mais, si vous utilisez bash (ou ksh, zsh ou yash), il existe un moyen plus sûr: utiliser un tableau:

  si some_condition
 puis
     criteria = (- type f) # Vous pourriez dire `criteria = (" - type "" f ")`, mais c'est vraiment inutile.
 autre
     criteria = () # Ne pas utiliser de guillemets sur cette command!
 Fi
 si some_other_condition
 puis
     critères + = (- mtime +42) # Note: pas `=`, mais ` + =`, pour append (append) à un tableau.
 Fi
 find "$ start_directory" "$ {criteria [@]}" other_ find _args 

De bash (1) ,

Tout élément d'un tableau peut être référencé en utilisant ${ name [ subscript ]} . … Si l' subscript est @ ou * , le mot s'étend à tous les membres du name . Ces indices ne diffèrent que lorsque le mot apparaît entre guillemets doubles. Si le mot est entre guillemets, … ${ name [@]} étend chaque élément du name à un mot séparé.

Ainsi, "${criteria[@]}" étend à (dans l'exemple ci-dessus) les zéro, deux ou quatre éléments du tableau des criteria , chacun étant cité. En particulier, si aucune de ces conditions n'est vraie, le tableau des criteria n'a pas de contenu (défini par l'instruction criteria=() ) et "${criteria[@]}" vaut rien (même pas une string vide incommode ).


Cela devient particulièrement intéressant et compliqué lorsque vous utilisez plusieurs mots, dont certains sont des inputs dynamics (user), que vous ne connaissez pas à l'avance et qui peuvent contenir des espaces ou d'autres caractères spéciaux. Considérer:

  printf "Entrez le nom du file à searchr:"
 lire fname
 si ["$ fname"! = ""]
 puis
     critères + = (- nom "$ fname")
 Fi 

Notez que $fname est cité à chaque fois qu'il est utilisé. Cela fonctionne même si l'user entre quelque chose comme foo bar ou foo* ; "${criteria[@]}" évalue à -name "foo bar" ou -name "foo*" . (Rappelez-vous que chaque élément du tableau est cité.)

Les arrays ne fonctionnent pas dans tous les shells POSIX; les arrays sont un ksh / bash / zsh / yash-ism. Sauf … il y a un tableau que tous les shells supportent: la list des arguments, alias "$@" . Si vous avez terminé avec la list d'arguments avec laquelle vous avez été invoqué (par exemple, vous avez copié tous les «parameters positionnels» (arguments) dans des variables ou les avoir traités), vous pouvez utiliser la list arg comme un tableau:

  si some_condition
 puis
     set - -type f # Vous pourriez dire `set -" -type "" f "`, mais c'est vraiment inutile.
 autre
     set --
 Fi
 si some_other_condition
 puis
     set - "$ @" -mtime +42
 Fi
 # De même: set - "$ @" -name "$ fname"
 find "$ start_directory" "$ @" other_ find _args 

La construction "$@" (qui est historiquement la première) a la même sémantique que "${ name [@]}" – elle étend chaque argument (ie chaque élément de la list d'arguments) à un mot séparé, comme si vous aviez tapé "$1" "$2" "$3" …

Extrait de la spécification POSIX Shell Command Language , sous 2.5.2 Paramètres spéciaux ,

@

    S'étend aux parameters de position, en commençant par un, en produisant initialement un champ pour chaque paramètre de position qui est défini. …, les champs initiaux doivent être conservés en tant que champs séparés, …. S'il n'y a pas de parameters de position, l'expansion de @ doit générer des champs nuls, même lorsque @ est entre guillemets; …

Le text complet est un peu énigmatique; le point key est qu'il spécifie que "$@" doit générer des champs zéro lorsqu'il n'y a pas de parameters de position. Note historique: quand "$@" été introduit pour la première fois dans le Bourne shell (prédécesseur de bash) en 1979, il avait un bug qui "$@" était remplacé par une seule string vide lorsqu'il n'y avait pas de parameters de position; voir Que signifie ${1+"$@"} dans un script shell, et en quoi diffère-t-il de "$@" ? , The Traditional Bourne Shell Family , Que signifie ${1+"$@"} … et où est-ce nécessaire? , et "$@" par rapport à ${1+"$@"} .


Les arrays aident également à la première situation:

  si some_condition
 puis
     ignorecase = (- i) # Vous pourriez dire `ignorecase = (" - i ")`, mais c'est vraiment inutile.
 autre
     ignorecase = () # Ne pas utiliser de guillemets sur cette command!
 Fi
 grep "$ {ignorecase [@]}" other_ grep _args 

____________________

PS (csh)

Cela devrait aller sans dire, mais, pour le bénéfice des gens qui sont nouveaux ici: csh, tcsh, etc., ne sont pas des coquilles Bourne / POSIX. Ils sont une famille complètement différente. Un cheval d'une couleur différente. Un autre jeu de balle. Une race différente de chat. Oiseaux d'une autre plume. Et, plus particulièrement, une autre boîte de vers.

Une partie de ce qui a été dit sur cette page s'applique à csh; par exemple: c'est une bonne idée de citer toutes vos variables à less d'avoir une bonne raison de ne pas le faire, et vous êtes sûr de savoir ce que vous faites. Mais, en csh, chaque variable est un tableau – il se trouve que presque chaque variable est un tableau d'un seul élément, et agit assez similaire à une variable shell ordinaire dans les shells Bourne / POSIX. Et la syntaxe est terriblement différente (et je veux dire terriblement ). Donc, nous ne dirons rien de plus sur les coquilles de la famille csh ici.

J'étais sceptique quant à la réponse de Stéphane, mais il est possible d'abuser de $ #:

 $ set `seq 101` $ IFS=0 $ echo $# 1 1 

ou $ ?:

 $ IFS=0 $ awk 'BEGIN {exit 101}' $ echo $? 1 1 

Ce sont des exemples artificiels, mais le potentiel existe.