记一次关于ssh远程执行命令的查错过程
事情是这样的我有一台服务器, 在服务器上运行着一个screen会话tunnel。现在需要使用脚本远程kill这个screen会话并重启。最开始的时候写了一个脚本以screen 名来kill 会话, 但最后发现有时候多个screen共用了一个session名,所以需要唯一的screen id来做退出处理。错误脚本如下:
SERVER=*.*.*.*
PORT=9999
KILL_AND_RESTART="
tunnel=`screen -ls | grep tunnel | grep -v grep | cut -d . -f 1`;
for x in $tunnel ;
do
echo kill screen $x;
screen -r $x -X quit;
done;
screen -dmS tunnel start_tunnel.sh -p $PORT 9.9.9.9 $PORT;
"
ssh root@$SERVER $KILL_AND_RESTART
但该脚本运行后”echo kill screen $x”并没有执行也就是说tunnel为空,本以为服务器不存在该session了, 但是实际登录服务器却发现出现了两个同名为tunnel的screen!说明以上脚本并未如期望般的运行。其实在shell脚本中单引号和双引号的区别很大,举个例子:
ANY=haha
echo "$ANY"
=> haha
echo '$ANY'
=> $ANY
可以看出单双引号下$ANY输出了不同的结果, 双引号输出了我们想要的结果haha, 单引号却直接输出了$ANY。也就是说双引号的内容在执行的时候会替换掉其中的命令单元传入执行结果, 而单引号则直接以原样输出。所以回到最开始的脚本中我使用了双引号来包裹脚本内容,当我在运行ssh root@$SERVER $KILL_AND_RESTART时KILL_AND_RESTART脚本字符串中的可执行单元`screen -ls | grep tunnel | grep -v grep | cut -d . -f 1`和$开头的取变量值不是在登录服务器时执行,而是在本地已经被带入生成最终内容。也就是说KILL_AND_RESTART在执行$取值时已变成
"
tunnel=;
for x in ;
do
echo kill screen ;
screen -r -X quit;
done;
screen -dmS tunnel start_tunnel.sh -p 9999 9.9.9.9 9999;
"
相当于占位符已被计算结果带入,这当然不是我想要的,当然知道原因后也就好解决了,稍微修改下脚本
SERVER=*.*.*.*
PORT=9999
KILL_AND_RESTART="
tunnel=\`screen -ls | grep tunnel | grep -v grep | cut -d . -f 1\`;
for x in \$tunnel ;
do
echo kill screen \$x;
screen -r \$x -X quit;
done;
echo listen at port $PORT;
screen -dmS tunnel start_tunnel.sh -p $PORT 9.9.9.9 $PORT;
"
echo $KILL_AND_RESTART;
ssh root@$SERVER $KILL_AND_RESTART
对于需要在服务器上带入的占位符要使用转义符”\”转义一下,注意这里PORT是要在本地接收命令行参数的(此处做了简化),也就是说其实该脚本中部分需要本地初始化部分需要在服务器上执行代入。这也是为什么不能直接使用单引号而用转义符的原因。执行ssh root@$SERVER $KILL_AND_RESTART之前的echo $KILL_AND_RESTART的打印结果
tunnel=`screen -ls | grep tunnel | grep -v grep | cut -d . -f 1`; for x in $tunnel ; do echo kill screen $x; screen -r $x -X quit; done; echo listen at port 9999; screen -dmS tunnel start_tunnel.sh -p 9999 9.9.9.9 9999;
也证明结果就是需要在服务器上执行的命令。