663 votes

Expansion des variables entre guillemets simples dans une commande en Bash

Je veux exécuter une commande à partir d'un bash script qui a des guillemets simples et quelques autres commandes à l'intérieur des guillemets simples et une variable.

par exemple repo forall -c '....$variable'

Dans ce format, $ est échappé et la variable n'est pas développée.

J'ai essayé les variantes suivantes, mais elles ont été rejetées :

repo forall -c '...."$variable" '

repo forall -c " '....$variable' "

" repo forall -c '....$variable' "

repo forall -c "'" ....$variable "'"

Si je substitue la valeur à la place de la variable, la commande est exécutée sans problème.

S'il vous plaît, dites-moi où je me trompe.

60 votes

repo forall -c ' ...before... '"$variable"' ...after...'

3 votes

@n.m. les guillemets simples font partie de la commande repo. je ne pense pas que cela fonctionnera.

1 votes

bash mange des guillemets simples. Soit vous n'êtes pas dans bash ou les guillemets simples ne font pas partie de l'adresse IP. repo commandement.

1015voto

Jo So Points 4602

À l'intérieur des guillemets simples, tout est conservé littéralement, sans exception.

Cela signifie que vous devez fermer les guillemets, insérer quelque chose, puis saisir à nouveau.

'before'"$variable"'after'
'before'"'"'after'
'before'\''after'

La concaténation des mots se fait simplement par juxtaposition. Comme vous pouvez le vérifier, chacune des lignes ci-dessus est un mot unique pour le shell. Les guillemets (simples ou doubles, selon la situation) n'isolent pas les mots. Ils ne sont utilisés que pour désactiver l'interprétation de divers caractères spéciaux, comme les espaces, $ , ; ... Pour un bon tutoriel sur la citation, voir la réponse de Mark Reed. Également pertinent : Quels sont les caractères qui doivent être échappés en bash ?

Ne pas concaténer les chaînes de caractères interprétées par un shell

Vous devez absolument éviter de construire des commandes shell en concaténant des variables. C'est une mauvaise idée similaire à la concaténation de fragments SQL (injection SQL !).

En général, il est possible d'avoir des espaces réservés dans la commande, et de fournir la commande avec des variables de sorte que le destinataire puisse les recevoir à partir de la liste des arguments d'invocation.

Par exemple, ce qui suit est très peu sûr. NE FAITES PAS CELA

script="echo \"Argument 1 is: $myvar\""
/bin/sh -c "$script"

Si le contenu de $myvar n'est pas fiable, voici un exploit :

myvar='foo"; echo "you were hacked'

Au lieu de l'invocation ci-dessus, utilisez des arguments positionnels. L'invocation suivante est meilleure -- elle n'est pas exploitable :

script='echo "arg 1 is: $1"'
/bin/sh -c "$script" -- "$myvar"

Notez l'utilisation de ticks simples dans l'affectation à script ce qui signifie qu'il est pris au pied de la lettre, sans expansion variable ni autre forme d'interprétation.

0 votes

@Jo.SO cela ne fonctionnera pas. car la commande repo ne prendra que les paramètres jusqu'à ce que les premiers guillemets soient fermés. Tout ce qui suit sera ignoré pour la commande repo et générera une autre erreur.

10 votes

@Rachit - le shell ne fonctionne pas comme ça. "some"thing' like'this est un seul mot pour la coquille. Les guillemets ne terminent rien en ce qui concerne l'analyse syntaxique, et il n'y a aucun moyen pour une commande appelée par la coquille pour dire ce qui a été cité comment.

3 votes

La deuxième phrase ("vous ne pouvez même pas échapper aux guillemets simples") fait des guillemets simples les trous noirs du Shell.

140voto

Mark Reed Points 23817

Le site repo la commande ne peut pas se soucier du genre de citations qu'elle reçoit. Si vous avez besoin d'une expansion des paramètres, utilisez des guillemets doubles. Si cela signifie que vous devez mettre des barres obliques inversées pour un grand nombre de choses, utilisez des guillemets simples pour la plus grande partie, puis sortez de ces guillemets et passez aux guillemets doubles pour la partie où vous avez besoin que l'expansion se produise.

repo forall -c 'literal stuff goes here; '"stuff with $parameters here"' more literal stuff'

L'explication suit, si vous êtes intéressés.

Lorsque vous exécutez une commande à partir du shell, cette commande reçoit comme arguments un tableau de chaînes de caractères à terminaison nulle. Ces chaînes peuvent contenir absolument tout caractère non nul.

Mais lorsque l'interpréteur de commandes construit ce tableau de chaînes de caractères à partir d'une ligne de commande, il interprète certains caractères de manière spéciale ; ceci est conçu pour rendre les commandes plus faciles (voire possibles) à taper. Par exemple, les espaces indiquent normalement la limite entre les chaînes de caractères dans le tableau ; pour cette raison, les arguments individuels sont parfois appelés "mots". Mais un argument peut néanmoins contenir des espaces ; vous devez simplement trouver un moyen d'indiquer à l'interpréteur de commandes que c'est ce que vous voulez.

Vous pouvez utiliser une barre oblique inversée devant n'importe quel caractère (y compris l'espace, ou une autre barre oblique inversée) pour indiquer à l'interpréteur de commandes de traiter ce caractère littéralement. Mais alors que vous pouvez faire quelque chose comme ceci :

echo \”That\'ll\ be\ \$4.96,\ please,\"\ said\ the\ cashier

...cela peut devenir fastidieux. Le shell propose donc une alternative : les guillemets. Il en existe deux grandes variétés.

Les guillemets doubles sont appelés "guillemets de regroupement". Ils empêchent les caractères de remplacement et les alias d'être développés, mais ils servent surtout à inclure des espaces dans un mot. D'autres choses, comme l'expansion des paramètres et des commandes (le genre de choses signalées par le signe $ ) se produisent toujours. Et bien sûr, si vous voulez une double citation littérale à l'intérieur d'une double citation, vous devez la barrer :

echo "\"That'll be \$4.96, please,\" said the cashier"

Les guillemets simples sont plus draconiens. Tout ce qui se trouve entre eux est pris au pied de la lettre, y compris les antislashs. Il n'y a absolument aucun moyen d'obtenir un guillemet simple littéral à l'intérieur de guillemets simples.

Heureusement, les guillemets dans le shell ne sont pas des délimiteurs de mots ; à elles seules, elles ne terminent pas un mot. Vous pouvez entrer et sortir des guillemets, y compris entre différents types de guillemets, dans un même mot pour obtenir le résultat souhaité :

echo '"That'\''ll be $4.96, please," said the cashier'

C'est donc plus facile - beaucoup moins de barres obliques inversées, même s'il faut s'habituer à la séquence close-single-quote, backslashed-literal-single-quote, open-single-quote.

Les shells modernes ont ajouté un autre style de citation non spécifié par la norme POSIX, dans lequel le guillemet simple de tête est préfixé par un signe dollar. Les chaînes de caractères ainsi citées suivent des conventions similaires à celles des chaînes de caractères de la version standard ANSI du langage de programmation C, et sont donc parfois appelées "chaînes ANSI" et le symbole de la chaîne de caractères de la version ANSI. $' ... ' paire "guillemets ANSI". Dans ces chaînes de caractères, les conseils ci-dessus concernant les barres obliques inversées à prendre au pied de la lettre ne s'appliquent plus. Au contraire, ils redeviennent spéciaux - non seulement vous pouvez inclure un guillemet simple littéral ou une barre oblique inversée en lui faisant précéder d'une barre oblique inversée, mais l'interpréteur de commandes développe également les échappements de caractères ANSI C (tels que \n pour un saut de ligne, \t pour l'onglet, et \xHH pour le caractère avec le code hexadécimal HH ). Sinon, cependant, ils se comportent comme des chaînes de caractères entre guillemets : aucune substitution de paramètre ou de commande n'a lieu :

echo $'"That\'ll be $4.96, please," said the cashier'

La chose importante à noter est que la chaîne unique reçue en tant qu'argument de la fonction echo La commande est exactement la même chose dans tous ces exemples. Une fois que l'interpréteur de commandes a fini d'analyser une ligne de commande, il n'y a aucun moyen pour la commande exécutée de savoir ce qui a été cité et comment. Même si elle le voulait.

3 votes

Oui. Je comprends maintenant. Cela fonctionne parfaitement. Merci beaucoup pour votre aide !

7 votes

Cette réponse était géniale.

1 votes

Est-ce que le $'string' est conforme au format POSIX ? En outre, existe-t-il un nom pour ce format ?

6voto

Manmohan Points 109

Voici ce qui a fonctionné pour moi -

QUOTE="'"
hive -e "alter table TBL_NAME set location $QUOTE$TBL_HDFS_DIR_PATH$QUOTE"

3 votes

J'espère que TBL_HDFS_DIR_PATH n'est pas une entrée fournie par l'utilisateur btw, cela permettrait une belle injection sql...

2 votes

Mettez QUOTE dans une variable séparée est complètement superflu ; les guillemets simples à l'intérieur des guillemets doubles sont juste un caractère normal. (De même, n'utilisez pas de majuscules pour vos variables privées).

4voto

Joelerr Points 70

utilisez simplement printf

au lieu de

repo forall -c '....$variable'

utiliser printf pour remplacer le jeton de variable par la variable développée.

Par exemple :

template='.... %s'

repo forall -c $(printf "${template}" "${variable}")

1 votes

C'est cassé ; printf ne vous apporte pas grand-chose ici, et le fait de ne pas citer la subsititution de la commande entraîne de nouveaux problèmes de citation.

2voto

Kevin Remo Points 507

EDIT : (Selon les commentaires en question :)

Je me suis penché sur la question depuis lors. J'ai eu la chance d'avoir le repo qui traînait. Je ne sais toujours pas s'il est nécessaire de placer les commandes entre guillemets simples. Je me suis penché sur la syntaxe de repo et je ne pense pas que ce soit nécessaire. Vous pouvez utiliser des guillemets doubles autour de votre commande, puis utiliser les guillemets simples et doubles dont vous avez besoin à l'intérieur, à condition d'échapper aux guillemets doubles.

0 votes

Merci Kevin pour votre aide.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X