This question already has answers here: Why does shell ignore quoting characters in arguments passed to it through variables? [duplicate] (3 answers) Closed 6 years ago.
I try to execute the following command :
mysql AMORE -u username -ppassword -h localhost -e "SELECT host FROM amoreconfig"
I store it in a string :
cmd="mysql AMORE -u username -ppassword -h localhost -e\"SELECT host FROM amoreconfig\""
Test it :
echo $cmd
mysql AMORE -u username -ppassword -h localhost -e"SELECT host FROM amoreconfig"
Try to execute by doing :
$cmd
And I get the help page of 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]
(...)
I guess I am doing something plain wrong with the quotes but can't find out what is the problem.
declare -f
; for an array (the typical "dynamically constructed" approach): printf '%q ' "${array[@]}"; echo
.
eval
, as the top answers here do, incurs substantial security risk (opening one up to shell injection attacks if any content is parameterized).
Have you tried:
eval $cmd
For the follow-on question of how to escape *
since it has special meaning when it's naked or in double quoted strings: use single quotes.
MYSQL='mysql AMORE -u username -ppassword -h localhost -e'
QUERY="SELECT "'*'" FROM amoreconfig" ;# <-- "double"'single'"double"
eval $MYSQL "'$QUERY'"
Bonus: It also reads nice: eval mysql query ;-)
Use an array, not a string, as given as guidance in BashFAQ #50.
Using a string is extremely bad security practice: Consider the case where password
(or a where clause in the query, or any other component) is user-provided; you don't want to eval
a password containing $(rm -rf .)
!
Just Running A Local Command
cmd=( mysql AMORE -u username -ppassword -h localhost -e "SELECT host FROM amoreconfig" )
"${cmd[@]}"
Printing Your Command Unambiguously
cmd=( mysql AMORE -u username -ppassword -h localhost -e "SELECT host FROM amoreconfig" )
printf 'Proposing to run: '
printf '%q ' "${cmd[@]}"
printf '\n'
Running Your Command Over SSH (Method 1: Using 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"
Running Your Command Over SSH (Method 2: Command Line)
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"
cmd+=( -e "$query" )
to append those arguments to the existing array, and be assured that query
will be added as a single argument to -e
that's passed to mysql
; no need to look into its contents to figure out if it spawns a subshell or escapes its quotes and launches a rootkit or whatever else.
try this
$ cmd='mysql AMORE -u root --password="password" -h localhost -e "select host from amoreconfig"'
$ eval $cmd
eval "$cmd"
, not eval $cmd
, to handle cases where any word-split component could be glob-expanded to a file in the current directory -- or cases where characters in IFS can't be substituted for others harmlessly.
You don't need the "eval" even. Just put a dollar sign in front of the string:
cmd="ls"
$cmd
`$cmd`
To eliminate the need for the cmd variable, you can do this:
eval 'mysql AMORE -u root --password="password" -h localhost -e "select host from amoreconfig"'
eval
at all, as opposed to just running mysql [...]
?
Success story sharing
eval
statement, when they become syntactic via use ofeval
, can have their effect undone by any literal quotes within the data; thus, they don't provide effective security.eval $cmd
, unlikeeval "$cmd"
, splits your input into words, evaluates each word as a glob, and then pastes them back together with spaces, so a command with a whitespace-surrounded asterisk in it could cause shell expansions in the current working directory's filenames to be evaluated).