在shell脚本中扩展变量

4

我有一个需要循环处理的查询。

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

以下行正确展开。
mysql -h$dbserver -u$username -p$password $dbname -Be"select '$dbserver' as server;" >> /home/develop/myreport.csv

但是,当我将查询语句取出并保存到变量中时(如上所示),它停止按预期工作,并为“dbserver”提供空值。 循环中提到的那行代码不起作用。 如何解决这个问题?

3个回答

2

如果你想在循环内部扩展查询中的'$dbserver',你可能会这样写:

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

原始写法中,查询字符串在任何值被赋给$dbserver之前就已经被评估了,这就是为什么输出结果为空字符串的原因。

请注意,输出重定向只在done行执行一次,而不是在循环中每次执行(这意味着您不需要再添加任何内容)。


通常情况下,可以使用eval将查询创建在循环外部。然而,由于$dbserver的值被包含在单引号内,所以这变得困难。如果您正在使用的DBMS允许在字符串周围使用双引号(与SQL标准相反),那么这将通过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

然后您可以通过将'"'替换为''\'''序列来使其使用单引号:

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

然而,这种引号序列会让理智的人们尖叫着离开房间 - 请原谅我一下,我要大声地离开这个地方![...稍后...]好多了!

解释:

  • 整个字符串在单引号内。
  • 在这样的字符串内没有转义字符。
  • 因此,第一个反斜杠只是一个反斜杠。
  • 接下来的4个字符是序列'\''
  • 这些引号中的第一个终止当前的单引号字符串。
  • 反斜杠暂停下一个字符的特殊含义,以便字符串包含实际的单引号从序列中的第二个单引号开始。
  • 第三个单引号开始一个新的单引号字符串。
  • 因此,在处理反斜杠和引号的第一个序列之后,该字符串包含一个反斜杠和一个单引号。
  • $dbserver此时只是普通文本。
  • 然后我们重复上一个序列,最终得到字符串中的第二个反斜杠引号对。
  • 直到行末的最后一个单引号都是正常的。

eval进程对字符串运行额外的扩展。反斜杠引号对被替换为单引号;插入了$dbserver的当前值。然后可以将其作为普通参数传递给命令。

eval的困难在于确保您不会获得意外的副作用。这在MySQL中是双重复杂的,因为它使用反引号来封闭用作标记的关键字。当然,这种符号与eval交互起来非常恶劣。但是,只要在整个查询周围加上单引号,并在每个反引号的位置上放置反斜杠反引号,就可以做到这一点:

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

虽然我不认为可以推荐这个,但是。


谢谢。我希望查询语句能够放在脚本的顶部并且不在循环中,因为这样以后编辑会更容易。 - shantanuo
@shantanuo:你可以做到 - 看看我的详细回答。我不确定所需的符号是否能给你想要的好处;它更复杂,以正确地查询。 - Jonathan Leffler

0
将查询分配放在循环内部。在循环之前,您的变量尚未定义(即为空)。

0
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

花括号并不是必需的 - 如果你使用它们,为了一致性,你在查询分配时也应该使用它们吧? - Jonathan Leffler
这是否意味着当我需要使用大括号时,为了保持一致性,我必须将所有的大括号都改成使用大括号?这些大括号只是以防万一而存在,没有任何害处。 - ghostdog74

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接