【问题标题】:command execute with error when use expect in shell script while work fine in pure shell在shell脚本中使用expect时命令执行出错,而在纯shell中工作正常
【发布时间】:2021-10-14 02:07:39
【问题描述】:

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

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

它像在纯 linux shell 中一样工作得很好,但是当我尝试使用 expect 来“发送”“基本”命令时发生错误(如下所示)。 (本地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 向我展示了basic rule for tcl,然后我意识到期望在发送实际命令之前会进行命令替换。我们可以通过执行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

【问题讨论】:

  • 你需要先学习tcl才能使用Expect。如果您对 shell 语法更熟悉,请尝试我的 sexpect
  • 实际错误是什么? (您似乎包含了指向 PNG 文件的链接而不是错误消息。)您可以使用 Ansible 或 Salt Stack 等通用自动化工具来代替编写此脚本吗?
  • 我是新手,甚至期望使用 shell,并且现在不打算像学习 Ansible 或 Salt Stack 那样深入。写这个脚本更像是为了好玩,LOL

标签: bash docker expect


【解决方案1】:

与 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

【讨论】:

  • 感谢您的回复。 document 验证了我期望为我做预解析(命令替换)的假设。由于我希望在远程机器上执行 docker 命令,我添加了反斜杠以将其视为普通字符。所以我的最终命令是:send {[ \$(docker ps -a | grep Exited | wc -l) -ne 0 ] &amp;&amp; docker start \$(docker ps -a | grep Exited | cut -d' ' -f1)}
  • 使用大括号意味着你不需要反斜杠。
  • emmm,事情是这样的,假设我有两台机器,它们的 ip 分别是 10.0.0.1 和 10.0.0.2。我在 10.0.0.1 上执行我的 shell 脚本(包含期望命令:spawn ssh user@10.0.0.2 send {echo $(hostname -I) }),这意味着我要打印 10.0.0.2。但是我得到了 10.0.0.1 ,只是我将命令更改为send {echo \$(hostname -I) }(带反斜杠)然后打印 10.0.0.2。
  • @zjh_Leo,带有未引用符号的 heredocs,例如 &lt;&lt;EOF,在 expect 看到它们之前总是由 shell 处理(所以它不是 expect运行命令替换和参数扩展,它是后来开始期望的 bash 的副本)。您需要使用 &lt;&lt;'EOF'&lt;&lt;\EOF 来防止这种情况。将 expect 替换为 cat 以查看您在 shell 完成操作后实际发送的源代码。
猜你喜欢
  • 2022-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-22
  • 2012-12-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多