295 votes

Comment exécuter une commande bash stockée comme une chaîne avec des guillemets et un astérisque ?

J'essaie d'exécuter la commande suivante :

mysql AMORE -u username -ppassword -h localhost -e "SELECT  host  FROM amoreconfig"

Je le stocke dans une chaîne :

cmd="mysql AMORE -u username -ppassword -h localhost -e\"SELECT  host  FROM amoreconfig\""

Testez-le :

echo $cmd
mysql AMORE -u username -ppassword -h localhost -e"SELECT host FROM amoreconfig"

Essayez d'exécuter en faisant :

$cmd

Et j'obtiens la page d'aide de mysql :

mysql  Ver 14.14 Distrib 5.1.31, for pc-linux-gnu (i686) using readline 5.1
Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This software comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to modify and redistribute it under the GPL license
Usage: mysql [OPTIONS] [database]
(...)

Je suppose que je fais quelque chose de mal avec les guillemets mais je n'arrive pas à trouver la cause du problème.

11 votes

Je vous recommande de lire ceci : mywiki.wooledge.org/BashFAQ/050

4 votes

@DennisWilliamson - excellent lien ; j'aime particulièrement ceci : " Si votre tête est tellement enfoncée dans votre cul que vous pensez toujours que vous devez écrire chaque commande que vous êtes sur le point d'exécuter avant de l'exécuter "Je me demande comment l'auteur de ce texte résoudrait un script où vous construisez une commande dynamiquement, et où vous voulez explicitement la répercuter - afin de demander à l'utilisateur "Voulez-vous exécuter cette commande ?" avant qu'elle ne soit exécutée ?

0 votes

@sdaau, cela dépend de l'approche utilisée parmi celles données dans la FAQ. Pour une fonction, on peut imprimer son texte avec declare -f ; pour un tableau (l'approche typique "construite dynamiquement") : printf '%q ' "${array[@]}"; echo .

438voto

slebetman Points 28276

Avez-vous essayé :

eval $cmd

Pour la question suivante, comment s'échapper * car il a une signification particulière lorsqu'il est nu ou dans des chaînes de caractères entre guillemets doubles : utilisez des guillemets simples.

MYSQL='mysql AMORE -u username -ppassword -h localhost -e'
QUERY="SELECT "'*'" FROM amoreconfig" ;# <-- "double"'single'"double"
eval $MYSQL "'$QUERY'"

Bonus : il lit également bien : eval mysql query ;-)

0 votes

Merci, ça marche. Comment puis-je sélectionner toutes les colonnes ? Comment puis-je échapper à '*' ?

0 votes

Il n'est pas nécessaire d'échapper à l'astérisque (*) dans ce cas.

0 votes

La première tentative avec les deux a échoué. J'ai dû utiliser eval $MYSQL \"$QUERY\" Mais la première renvoie +---+ | * | +---+ | * | (...) Et la seconde avec l'échappement échoue avec : ERROR at line 1 : Commande inconnue '*'. Merci quand même pour votre aide :)

67voto

Charles Duffy Points 34134

Utilisez un tableau, et non une chaîne de caractères, comme indiqué à titre indicatif dans le document BashFAQ #50 .

L'utilisation d'une chaîne de caractères est une pratique de sécurité extrêmement mauvaise : Considérons le cas où password (ou une clause where dans la requête, ou tout autre composant) est fourni par l'utilisateur ; vous ne voulez pas eval un mot de passe contenant $(rm -rf .) !


Exécution d'une commande locale

cmd=( mysql AMORE -u username -ppassword -h localhost -e "SELECT  host  FROM amoreconfig" )
"${cmd[@]}"

Imprimer votre commande sans ambiguïté

cmd=( mysql AMORE -u username -ppassword -h localhost -e "SELECT  host  FROM amoreconfig" )
printf 'Proposing to run: '
printf '%q ' "${cmd[@]}"
printf '\n'

Exécution de votre commande via SSH (Méthode 1 : utilisation de Stdin)

cmd=( mysql AMORE -u username -ppassword -h localhost -e "SELECT  host  FROM amoreconfig" )
printf -v cmd_str '%q ' "${cmd[@]}"
ssh other_host 'bash -s' <<<"$cmd_str"

Exécution de votre commande via SSH (Méthode 2 : ligne de commande)

cmd=( mysql AMORE -u username -ppassword -h localhost -e "SELECT  host  FROM amoreconfig" )
printf -v cmd_str '%q ' "${cmd[@]}"
ssh other_host "bash -c $cmd_str"

3 votes

Considérez le cas où aucun mot de passe ne figure dans la requête. Pensez à l'utilité d'un tel système.

8 votes

@DavidBeckwith, pourquoi l'approche avec failles de sécurité est-elle plus utile que celle sans failles ? Quel avantage ajoute-t-elle ? Vous pouvez toujours construire dynamiquement vos tableaux ; vous avez simplement l'avantage de ne pas risquer que leur contenu soit analysé d'une manière différente de celle prévue.

4 votes

...c'est-à-dire : On peut courir cmd+=( -e "$query" ) pour ajouter ces arguments au tableau existant, et être assuré que query sera ajouté comme un seul argument à -e qui est transmis à mysql Il n'est pas nécessaire d'examiner son contenu pour savoir s'il génère un sous-shell ou s'il échappe aux guillemets et lance un rootkit ou autre.

26voto

ghostdog74 Points 86060

Essayez ceci

$ cmd='mysql AMORE -u root --password="password" -h localhost -e "select host from amoreconfig"'
$ eval $cmd

1 votes

Merci, ça marche. J'ai marqué l'autre réponse comme étant la réponse acceptée parce qu'elle était antérieure.

4 votes

Dans la mesure où cela fonctionne, cela fonctionne mal . Doit être eval "$cmd" pas eval $cmd pour gérer les cas où n'importe quel composant de découpage de mots pourrait être étendu de manière globale à un fichier dans le répertoire courant -- ou les cas où les caractères dans IFS ne peuvent pas être substitués à d'autres de manière inoffensive.

4 votes

Cette solution présente une vulnérabilité de sécurité par injection de commande si l'une des entrées de la chaîne évaluée est fournie par l'utilisateur. La solution proposée par @CharlesDuffy est bien meilleure.

6voto

David Points 61

Vous n'avez même pas besoin du "eval". Mettez juste un signe de dollar devant la chaîne :

cmd="ls"
$cmd

11 votes

Ne fonctionne que pour des commandes extrêmement simples ; ne fonctionne pas pour celle que le PO a donnée dans son exemple.

2 votes

Lire mywiki.wooledge.org/BashFAQ/050 pour comprendre pourquoi.

0 votes

Cela a fonctionné pour moi pour un outil nodejs au lieu de `$cmd`

-3voto

Paul Havens Points 13

Pour éliminer le besoin de la variable cmd, vous pouvez faire ceci :

eval 'mysql AMORE -u root --password="password" -h localhost -e "select host from amoreconfig"'

4 votes

Si vous vous contentez d'avoir un littéral codé en dur, pourquoi utiliser eval du tout, par opposition à l'exécution mysql [...] ?

1 votes

Eval avec une constante littérale n'a aucun sens !

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