La norme POSIX impose que l'expansion des mots se fasse dans l'ordre suivant (c'est moi qui souligne):
L'expansion Tilde (voir Expansion Tilde), l' expansion des paramètres (voir Expansion des paramètres), la substitution de commande (voir Substitution de commande) et l'expansion arithmétique (voir Expansion arithmétique) doivent être effectuées, du début à la fin. Voir l'élément 5 dans la reconnaissance des jetons.
Le fractionnement de champ (voir Découpage de champ) doit être effectué sur les parties des champs générés par l'étape 1, sauf si IFS est nul.
L'extension de nom de chemin (voir Expansion de nom de chemin) doit être effectuée, à moins que l'ensemble -f ne soit en vigueur.
La suppression de devis (voir Suppression de devis) doit toujours être effectuée en dernier.
Le seul point qui nous intéresse ici est le premier: comme vous pouvez le voir, l'expansion du tilde est traitée avant l'expansion des paramètres:
- Le shell tente une expansion de tilde
echo $x
, il n'y a pas de tilde à trouver, alors il continue.
- Le shell tente une expansion des paramètres
echo $x
, $x
est trouvé et développé et la ligne de commande devient echo ~/someDirectory
.
- Le traitement continue, l'expansion du tilde ayant déjà été traitée, le
~
personnage reste tel quel.
En utilisant les guillemets lors de l'attribution du $x
, vous demandiez explicitement de ne pas développer le tilde et de le traiter comme un caractère normal. Une chose souvent manquée est que dans les commandes shell, vous n'avez pas à citer toute la chaîne, vous pouvez donc faire en sorte que l'expansion se produise pendant l'affectation des variables:
user@host:~$ set -o xtrace
user@host:~$ x=~/'someDirectory'
+ x=/home/user/someDirectory
user@host:~$ echo $x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
Et vous pouvez également faire en sorte que l'expansion se produise sur la echo
ligne de commande tant qu'elle peut se produire avant l' expansion des paramètres:
user@host:~$ x='someDirectory'
+ x=someDirectory
user@host:~$ echo ~/$x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
Si, pour une raison quelconque, vous devez vraiment affecter le tilde à la $x
variable sans expansion et pouvoir le développer à la echo
commande, vous devez procéder deux fois pour forcer deux extensions de la $x
variable à se produire:
user@host:~$ x='~/someDirectory'
+ x='~/someDirectory'
user@host:~$ echo "$( eval echo $x )"
++ eval echo '~/someDirectory'
+++ echo /home/user/someDirectory
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
Cependant, sachez que selon le contexte dans lequel vous utilisez une telle structure, cela peut avoir un effet secondaire indésirable. En règle générale, préférez éviter d'utiliser quoi que ce soit nécessitant eval
lorsque vous avez un autre moyen.
Si vous souhaitez traiter spécifiquement le problème du tilde par opposition à tout autre type d'expansion, une telle structure serait plus sûre et portable:
user@host:~$ x='~/someDirectory'
+ x='~/someDirectory'
user@host:~$ case "$x" in "~/"*)
> x="${HOME}/${x#"~/"}"
> esac
+ case "$x" in
+ x=/home/user/someDirectory
user@host:~$ echo $x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
Cette structure vérifie explicitement la présence d'un interligne ~
et le remplace par le répertoire home de l'utilisateur s'il est trouvé.
À la suite de votre commentaire, cela x="${HOME}/${x#"~/"}"
peut en effet être surprenant pour quelqu'un qui n'est pas utilisé dans la programmation shell, mais est en fait lié à la même règle POSIX que j'ai citée ci-dessus.
Comme imposé par la norme POSIX, la suppression des devis se produit en dernier et l'expansion des paramètres se produit très tôt. Ainsi, ${#"~"}
est évalué et développé bien avant l'évaluation des citations externes. À tour de rôle, comme défini dans les règles d' extension des paramètres :
Dans chaque cas où une valeur de mot est nécessaire (en fonction de l'état du paramètre, comme décrit ci-dessous), le mot doit être soumis à l'expansion tilde, à l'expansion des paramètres, à la substitution de commande et à l'expansion arithmétique.
Ainsi, le côté droit de l' #
opérateur doit être correctement cité ou échappé pour éviter l'expansion du tilde.
Donc, pour le dire différemment, lorsque l'interpréteur de shell regarde x="${HOME}/${x#"~/"}"
, il voit:
${HOME}
et ${x#"~/"}
doit être étendu.
${HOME}
est étendu au contenu de la $HOME
variable.
${x#"~/"}
déclenche une expansion imbriquée: "~/"
est analysé mais, étant cité, est traité comme un littéral 1 . Vous auriez pu utiliser des guillemets simples ici avec le même résultat.
${x#"~/"}
expression elle-même est désormais développée, ce qui entraîne la ~/
suppression du préfixe de la valeur de $x
.
- Le résultat de ce qui précède est maintenant concaténé: l'expansion de
${HOME}
, le littéral /
, l'expansion ${x#"~/"}
.
- Le résultat final est entouré de guillemets doubles, empêchant fonctionnellement la division des mots. Je dis fonctionnellement ici parce que ces guillemets doubles ne sont pas techniquement nécessaires (voir ici et là par exemple), mais en tant que style personnel dès qu'une affectation va au-delà,
a=$b
je trouve généralement plus clair d'ajouter des guillemets doubles.
Soit dit en passant, si vous regardez de plus près la case
syntaxe, vous verrez la "~/"*
construction qui repose sur le même concept que x=~/'someDirectory'
j'ai expliqué ci-dessus (ici encore, des guillemets simples et doubles pourraient être utilisés de manière interchangeable).
Ne vous inquiétez pas si ces choses peuvent sembler obscures à première vue (peut-être même à la seconde vue ou plus tard!). À mon avis, l'expansion des paramètres est, avec les sous-coquilles, l'un des concepts les plus complexes à comprendre lors de la programmation en langage shell.
Je sais que certaines personnes peuvent être en désaccord vigoureux, mais si vous souhaitez apprendre la programmation shell plus en profondeur, je vous encourage à lire le Guide de script avancé Bash : il enseigne le script Bash, donc avec beaucoup d'extensions et de cloches-et- sifflets par rapport aux scripts shell POSIX, mais je l'ai trouvé bien écrit avec beaucoup d'exemples pratiques. Une fois que vous gérez cela, il est facile de vous limiter aux fonctionnalités POSIX lorsque vous en avez besoin, je pense personnellement qu'entrer directement dans le domaine POSIX est une courbe d'apprentissage abrupte inutile pour les débutants (comparer mon remplacement de tilde POSIX avec Bash de type regex @ m0dular équivalent pour avoir une idée de ce que je veux dire;)!).
1 : Ce qui m'amène à trouver un bug dans Dash qui n'implémente pas correctement l'extension tilde ici (vérifiable à l'aide x='~/foo'; echo "${x#~/}"
). L'extension des paramètres est un domaine complexe tant pour l'utilisateur que pour les développeurs de shell eux-mêmes!
x='~'; print -l ${x} ${~x}
. J'ai abandonné après avoir fouillé lebash
manuel pendant un certain temps.