首页 > 解决方案 > 在shell脚本中使用expect时命令执行出错,而在纯shell中工作正常

问题描述

我尝试启动部署在单独服务器中的所有退出的 docker 容器,所以基本上我应该执行下面的基本命令

[ $(docker ps -a | grep Exited | wc -l) -ne 0 ] && docker start $(docker ps -a | grep Exited | cut -d' ' -f1)

它像在纯 linux shell 中一样工作得很好,但是当我尝试使用期望“发送”“基本”命令时发生错误(如下所示)。(本地ip是241,远端是209)

[root@localhost start_shell_dir]# spawn ssh root@192.168.1.209
root@192.168.1.209's password:
Last login: Fri Oct 15 22:23:25 2021 from 192.168.1.241
[root@localhost ~]# invalid command name "0"
    while executing
"0 -ne 0 "
    invoked from within
"send "[ 0 -ne 0 ] && docker start ""

错误日志显示我已经登录远程机器,执行 docker 命令时出现问题。 评论区的Glenn jackman向我展示了tcl 的基本规则,然后我意识到在发送真正的命令之前,expect 会进行命令替换。我们可以从 execute 中看到bash -x script.sh

[root@localhost start_shell_dir]# bash -x startContainer.sh
+ read ip pass
+ read ip pass
+ /usr/bin/expect
[root@localhost start_shell_dir]# ++ docker ps -a
++ grep Exited
++ wc -l
++ docker ps -a
++ grep Exited
++ cut '-d ' -f1
spawn ssh root@192.168.1.209
root@192.168.1.209's password:
Last login: Fri Oct 15 22:37:56 2021 from 192.168.1.241
[root@localhost ~]# invalid command name "0"
    while executing
"0 -ne 0 "
    invoked from within
"send "[ 0 -ne 0 ] && docker start ""

无论如何,对我有用的最后一个命令是下面显示的命令(用大括号替换双引号并在 $() 之前用反斜杠将其保留为普通字符,而不是预先解析它)。

send {[ \$(docker ps -a | grep Exited | wc -l) -ne 0 ] && docker start \$(docker ps -a | grep Exited | cut -d' ' -f1)}
#!/bin/bash
# my original script with error
while read ip pass
do
                {
                /usr/bin/expect <<-END
                spawn ssh root@$ip
                expect {
                "yes/no" { send "yes\r";exp_continue }
                "password:" { send "$pass\r" }
                }
                expect "#"
                send "[ $(docker ps -a | grep Exited | wc -l) -ne 0 ] && docker start $(docker ps -a | grep Exited | cut -d' ' -f1)"
                expect eof
                END
                }&

done<apps_ip.txt

标签: bashdockerexpect

解决方案


与 shell 一样,Tcl(和期望)允许使用双引号进行插值。Tcl 使用方括号进行命令替换(与 shell 使用的方式相同$(...)

使用花括号来保护该字符串的内容(类似于 shell 的单引号):

send {[ $(docker ps -a | grep Exited | wc -l) -ne 0 ] && docker start $(docker ps -a | grep Exited | cut -d' ' -f1)}
#....^.............................................................................................................^
# and don't forget to hit Enter
send "\r"

有关 Tcl 的一些语法规则,请参见https://www.tcl-lang.org/man/tcl8.6/TclCmd/Tcl.htm


推荐阅读