Lorsque j'ouvre une invite bash et tape:
$ set -o xtrace
$ x='~/someDirectory'
+ x='~/someDirectory'
$ echo $x
+ echo '~/someDirectory'
~/someDirectory
J'espérais que la 5ème ligne ci-dessus aurait disparu + echo /home/myUsername/someDirectory
. Y a-t-il un moyen de faire cela? Dans mon script Bash d'origine, la variable x est en fait remplie à partir des données d'un fichier d'entrée, via une boucle comme celle-ci:
while IFS= read line
do
params=($line)
echo ${params[0]}
done <"./someInputFile.txt"
Pourtant, j'obtiens un résultat similaire, avec le echo '~/someDirectory'
au lieu de echo /home/myUsername/someDirectory
.
x='~'; print -l ${x} ${~x}
. J'ai abandonné après avoir fouillé lebash
manuel pendant un certain temps.Réponses:
La norme POSIX impose que l'expansion des mots se fasse dans l'ordre suivant (c'est moi qui souligne):
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:
echo $x
, il n'y a pas de tilde à trouver, alors il continue.echo $x
,$x
est trouvé et développé et la ligne de commande devientecho ~/someDirectory
.~
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: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:Si, pour une raison quelconque, vous devez vraiment affecter le tilde à la
$x
variable sans expansion et pouvoir le développer à laecho
commande, vous devez procéder deux fois pour forcer deux extensions de la$x
variable à se produire: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:
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 :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
.${HOME}
, le littéral/
, l'expansion${x#"~/"}
.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 quex=~/'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!la source
x="${HOME}/${x#"~/"}"
? Il ressemble à une concaténation de 3 chaînes:"${HOME}/${x#"
,~/
et"}"
. Le shell autorise-t-il les guillemets doubles imbriqués lorsque la paire intérieure de guillemets doubles se trouve à l'intérieur d'un${ }
bloc?Une réponse possible:
Puisque vous lisez l'entrée d'un fichier, je ne le ferais pas.
Vous pouvez rechercher et remplacer le ~ par la valeur de $ HOME, comme ceci:
Donne moi:
la source
${parameter/pattern/string}
extension est une extension Bash et peut ne pas être disponible dans d'autres shells.case
structure POSIX illustre bien comment les scripts Bash sont plus conviviaux, spécialement pour les débutants.