在 Bash 脚本中执行 Mysql 指令的问题

为了排查问题, 写了一个简单的bash脚本. 脚本从命令行接受参数, 然后从数据库中查询相应的结果, 再进行简单的计算. 自己不只一次在查询数据库这里 出错了. 主要的原因是没有搞清楚$(expr)的执行情况, bash中引号的作用以及惯性思维. 在这里 做个简单的总结, 希望以后不要再错了…..

模拟一下

我们用下面这个bash脚本来模拟一下:

right
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash

if [[ -z $1 ]]; then
    echo "usage: try.sh"
    exit 0
fi

PRE_CMD='mysql -hhometown -usally -pbestwishes -Ddoor -N -B -e '

SQL="select 'get: $1'"

result=$($PRE_CMD "$SQL")

echo $result

脚本就是从命令行读取然后通过数据库再打印出来(会不会很无聊?), 这里最关键的就是result=$($PRE_CMD "$SQL")这句. OK, 按照简单的理解就是把两个变量 展开, 然后bash将这个完整的字符串进行解析然后执行, 就像perl中的``一样. 很遗憾, 这里 真的不是这样子的.

在命令行中, 我一般会这样执行sql指令:

cmd
1
mysql -uroot -pxxxx -Dyyyy -N -B -e'select * from zzzz where name = "foo"'

同样的, 如果是在perl(或者其他语言)中, 我会把前半部分搞成一个变量, sql语句部分搞成一个变量, 查询参数搞成 一个变量, 然后拼起来使用``或者system执行. 但是在bash中不行. 我们可以试下这个脚本:

wrong
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
### perl fine

perl -e 'my $cmd = "mysql -usally -pbestwishes -Ddoor \
         -B -N -e "; my $sql= "\"select 1\""; $r=`$cmd $sql`; print $r'

### bash bad

#!/bin/bash

if [[ -z $1 ]]; then
    echo "usage: try.sh"
    exit 0
fi

PRE_CMD='mysql -hhometown -usally -pbestwishes -Ddoor -N -B -e '

# different here
SQL="\"select 'get: $1'\""

result=$($PRE_CMD $SQL)

echo $result

这个就是我原先习惯的写法, 然后每次执行都是打印出mysql的帮助信息. 起初我以为是引号的问题, 但是 经过N次尝试双引号和单引号的各种排列组合, 都以失败告终. 好吧, 还是回归原理看一下吧.(google下发现没有人 这么使用bash, 难道是我太奇葩?)

向 man 寻求帮助

这里最关键的一点是: bash中的引号仅仅是作为一个界定符, 表明是否需要进行扩展.

QUOTING

Quoting is used to remove the special meaning of certain characters or words to the shell. Quoting can be used to disable special treatment for special characters, to prevent reserved words from being recognized as such, and to prevent parameter expansion.

接下来是几个关键的expansion说明:

EXPANSION

Expansion is performed on the command line after it has been split into words. There are seven kinds of expansion performed: brace expansion, tilde expansion, parameter and variable expansion, command substitution, arithmetic expansion, word splitting, and pathname expansion.

The order of expansions is: brace expansion, tilde expansion, parameter, variable and arithmetic expansion and command substitution (done in a left-to-right fashion), word splitting, and path‐name expansion.

还有这么一句:

Quote Removal

After the preceding expansions, all unquoted occurrences of the characters \, ‘, and “ that did not result from one of the above expansions are removed.

所以我们应该采用第一种形式, $($CMD "$SQL"), 这里执行流程是先进行变量扩展, 然后进行分词(引号的作用是将$SQL整个作为一个word).

这里再提一点: `expr`$(expr)的不同之处在于对于\的解释.

PPPPSSSS

得知两个消息:

  1. moon 又有比赛了, 拿了WGT的第二名(第一名是lyn), 祝福moon. (moon是谁? hmmmm.. moon 是神). 貌似今年wcg仍然有war3的项目(邀请赛?), 加油哈~
  2. 麻美ゆま得了癌症, 现在一直在治疗. 希望她能坚持下来, 早日康复… best wishes …

Comments