views:

25

answers:

3

I have a query that I need to loop.

query="select '$dbserver' as server;"

while read dbserver username password dbname type
do
mysql -h$dbserver -u$username -p$password $dbname -Be"$query" >> /home/develop/myreport.csv
done < $dblist

The following line expands correctly.

mysql -h$dbserver -u$username -p$password $dbname -Be"select '$dbserver' as server;" >> /home/develop/myreport.csv

But when I take out the query and save it in a variable as shown above, it stops working as expected and gives blank value for "dbserver". The line mentioned in the loop above does not work. How to correct this problem?

A: 

Put the query assignment inside the loop. Before the loop your variables are not defined yet (i.e. blank)

spbfox
A: 
while read -r dbserver username password dbname type
do
 query="select '$dbserver' as server;"
 mysql -h${dbserver} -u${username} -p${password} ${dbname} -Be"${query}" >> /home/develop/myreport.csv
done < $dblist
ghostdog74
The braces are not really necessary - and shouldn't you use them in the assignment to query, for consistency, if you use them at all?
Jonathan Leffler
does it mean that when I need to use the braces, I must change all of them to use braces, for consistency? The braces are there just in case only. Its no harm.
ghostdog74
+2  A: 

If you want the '$dbserver' in the query expanded inside the loop, you'd probably write:

while read dbserver username password dbname type
do
    query="select '$dbserver' as server;"
    mysql -h$dbserver -u$username -p$password $dbname -Be"$query"
done < $dblist  > /home/develop/myreport.csv

As originally written, the query string is evaluated before any value is assigned to $dbserver, which is why you got the empty string in the output.

Note that the output redirection was done just once - on the done line rather than each time in the loop (which means that you don't need append any more).


Getting the query created outside the loop is normally something you can do, using eval. However, because the value of the $dbserver is enclosed inside single quotes, this turns out to be hard. If the DBMS you are using permits the use of double quotes around strings (contrary to the SQL standard), then this works with an eval:

query='select \"$dbserver\" as server;'
echo "$query"
while read dbserver username password dbname type
do
    echo 1: "$query"
    eval echo 2: "$query"
    qval=$(eval echo "$query")
    echo mysql -h$dbserver -u$username -p$password $dbname -Be"$qval"
done

You can then adapt this to use single quotes by replacing the '"' with the ''\''' sequence:

query='select \'\''$dbserver\'\'' as server;'
echo "$query"
while read dbserver username password dbname type
do
    echo 1: "$query"
    eval echo 2: "$query"
    qval=$(eval echo "$query")
    echo mysql -h$dbserver -u$username -p$password $dbname -Be"$qval"
done

That is, however, the sort of quote sequence that sends sane people from the room screaming - excuse me a moment while I vacate the premises noisily! [...later...] That's better!

Explanation:

  • The overall string is inside single quotes.
  • There are no escape characters inside such a string.
  • Therefore, the first backslash is just a backslash.
  • The next 4 characters are the sequence '\''.
  • The first of these quotes terminates the current single-quoted string.
  • The backslash suspends the special meaning of the next character, so that the string contains an actual single quote from the second single quote in the sequence.
  • The third single quote starts a new single-quoted string.
  • So, after processing the first sequence of backslashes and quotes, the string contains a backslash and a single quote.
  • $dbserver is just normal text at this point.
  • We then have a repeat of the previous sequence, ending up with a second backslash-quote pair in the string.
  • Everything is normal to the last single quote on the line.

The eval process runs an extra lot of expansion on the string. The backslash-quote pairs are replaced by just quote; the current value of $dbserver is inserted. This can then be passed to the command as an ordinary argument.

The difficulty with eval is ensuring that you don't get unexpected side-effects. This is doubly complex with MySQL which uses back-quotes to enclose keywords used as tokens. That notation interacts diabolically with eval, of course. However, with single quotes around the whole query and backslash-backquote in place of each backquote, you can do it:

query='select \'\''$dbserver\'\'' as server, \`ls\` as column;'
echo "$query"
while read dbserver username password dbname type
do
    echo 1: "$query"
    eval echo 2: "$query"
    qval=$(eval echo "$query")
    echo mysql -h$dbserver -u$username -p$password $dbname -Be"$qval"
done

I don't think it can be recommended, though.

Jonathan Leffler
Thanks. I wanted the query to be placed at the top of the script and out of loop because it would have been easier to edit it later.
shantanuo
@shantanuo: you can do it - see my expanded answer. I'm not sure whether the notation required gives you the benefit you seek; it is more complex to get the query right.
Jonathan Leffler