期望脚本的引用问题

Quoting issue with expect script

我有一个自动连接到 VPN 的脚本,因为我经常需要一天连接和断开几次。 (ScreenHero 和 GoToMeeting 等某些东西在 VPN 上失败,邮件服务器和其他东西在 VPN 上被阻止,但是我无法连接到 Git 服务器,除非我在 VPN 上,除非我在 VPN 上,否则我正在开发的应用无法访问某些后端服务,因此在断开连接时开发会受到限制。)

该脚本减少了麻烦,但我的密码中有一个 $(可通过环境变量 VPN_PASSWORD1 在环境中使用) ), 并且密码值在通过 expect 传递给 VPN 程序之前被插入。

#!/bin/bash

{
  /usr/bin/expect << END_OF_LOGIN_SESSION
  set timeout 30
  spawn /opt/cisco/anyconnect/bin/vpn -s connect blah.blahblah.com
  expect "Username:*"
  send "\r"
  expect "Password:*"
  send "$VPN_PASSWORD\r"
  expect "accept? \[y/n\]:*"
  send "y\r"
  expect eof
END_OF_LOGIN_SESSION
}

如何按字面意思传递密码,而不让它受到字符串插值的影响?

更改我的密码是一种逃避。用 \ 保存它也是一种逃避(和可怕的用户体验,因为每次我为这个项目 2[=37= 获取我的 .project 文件时我都必须输入它]).

看来expect一定有解决办法。


  1. 是的,我知道有人偷了我的笔记本电脑可以尝试登录,但是 (a) 他们必须先进入我的帐户,并且 (b) 如果没有我的 PIN,他们会失败,由 phone 输入,无论如何。

  2. 不,密码没有保存到磁盘。我在与该项目相关的每个 shell 会话中输入一次。

除非您定义了具有该名称的任何变量,否则您应该首先获得 can't read "VPN_PASSWORD": no such variable

可以通过Tcl的特殊数组变量访问环境变量env

您可以直接使用 env(VPN_PASSWORD) 访问相同的内容。

send "$env(VPN_PASSWORD)\r"

参考: env

您在未加引号的 heredoc 中有 shell 变量 $VPN_PASSWORD,因此 shell 将替换该值。假设 VPN_PASSWORD='foo$bar' => 那么 expect 会看到:send "foo$bar\r" 并且你会得到 can't read "bar": no such variable。解决方案是使用 {braces} 而不是双引号,这样 expect 将 不会 尝试扩展 "inner" 变量。

send {$VPN_PASSWORD}; send "\r"

你需要一个单独的 send "\r" 因为把 \r 放在花括号里会去掉它的特殊意义,Tcl 不会让你做 send {$VPN_PASSWORD}"\r"

这是一个演示:

$ VPN_PASSWORD='foo$bar'
$ expect <<END
send_user "$VPN_PASSWORD\n"
END
can't read "bar": no such variable
    while executing
"send_user "foo$bar\n""
$ expect <<END
send_user {$VPN_PASSWORD}; send_user "\n"
END
foo$bar

在 Tcl 中,大括号的作用类似于 shell 中的单引号:其中的所有内容都是文字字符。


使用环境传递值可能更干净。这里它被实现为一个shell函数

vpnconnect() {
    expect <<'END'
        set timeout 30
        spawn /opt/cisco/anyconnect/bin/vpn -s connect $env(VPN_HOST)
        expect "Username:*"
        send "\r"
        expect "Password:*"
        send "$env(VPN_PASSWORD)\r"
        expect {accept? [y/n]:*}
        send "y\r"
        expect eof
END
}