在 expect 脚本中输入 sudo 密码

3

我有一个期望代码在bash脚本中,类似于这样

env input1=${INPUT1} input2=${INPUT2} expect << "EOS"
    set timeout -1
    spawn ./another_script.sh
    expect {
        "Input 1" { send -- "$env(input1)\r";exp_continue }
        "Input 2" { send -- "$env(input2)\r";exp_continue }
        eof
    }
EOS

在执行 another_script.sh 过程中,我需要输入 sudo 密码,但由于还在 expect 中,无法输入。 有什么办法可以让我输入 sudo 密码并使脚本继续运行? 我不想将密码保存在脚本中然后传递给 expect,而是想直接输入密码。

2
让用户交互式地控制是Expect interact功能的用途。 - Charles Duffy
我找不到一个好的/可用的interact使用示例。我尝试了类似于这样的代码 "password for" { interact -o "\r" return ; exp_continue },但是有一个问题是它会显示我正在输入什么,另一个问题是在我按下ENTER键后,没有任何反应,也就是说脚本没有继续工作。 - adamm
2
如果您想要与“expect”相关的专家参与讨论,我建议添加一个“tcl”标签。因为“expect”是TCL语言的扩展,而不是bash的一部分。由于您的问题与如何从bash启动expect无关,而与其启动后的行为有关,所以这并不是一个特别针对bash的问题。 - Charles Duffy
4个回答

4

Donald Fellows的回答是正确的方法,但为了完整起见,这里提供你试图寻找的“interact”解决方案。要使用的命令是:

expect {
    "Input 1" { send -- "$env(input1)\r"; exp_continue }
    "Input 2" { send -- "$env(input2)\r"; exp_continue }
    "password for" { stty -echo
                    interact -u tty_spawn_id -o "\r" return
                    stty echo
                    exp_continue }
    eof
}

你面临的问题是你正在使用stdin运行expect脚本,因此交互会有困难,除非你使用-u tty_spawn_id/dev/tty和用户一起工作。你需要显式地在此tty上设置回显开/关状态,因为sudo只会在生成的命令和expect之间的pty上进行操作。

3
处理此类事件的标准方式是等待sudo密码提示,提供密码,然后继续操作。但对于长时间运行的脚本,您需要在expect脚本中缓存密码,以便在几个小时内不必多次向用户请求密码。实际上,为了可用性,您可能需要事先询问密码,而不是等到底层系统需要它。
幸运的是,expect包括对stty的支持,因此添加它很容易。
env input1=${INPUT1} input2=${INPUT2} expect << "EOS"
    # Prompt for password, cribbed/converted from example on expect(1) manpage
    stty -echo
    send_tty "sudo password: "
    expect_tty -re "(.*)\n"
    send_tty "\n"
    set password $expect_out(1,string)
    stty echo

    # Rest of the script, with clause for sending the password at the right time
    set timeout -1
    spawn ./another_script.sh
    expect {
        "Input 1" { send -- "$env(input1)\r"; exp_continue }
        "Input 2" { send -- "$env(input2)\r"; exp_continue }
        "assword: " { send -- "$password\r"; exp_continue }
        eof
    }
EOS

感谢您的评论。我尝试了您的建议,但我遇到了这个错误sudo password: send: spawn id exp0 not open while executing "send_user "\n""您提到我可以观察提示符,供应并继续执行。 我该如何做?我认为这将是原始问题的答案。 - adamm
可能是你将expect脚本包装在bash脚本中的方式(我刚从你那里复制过来)。我仍然不知道为什么编写expect脚本的人喜欢这样做。 - Donal Fellows
这是因为我不想再为expect脚本创建一个文件。我有一个很大的bash脚本,其中我期望有一些提示(Input 1和Input 2)。Expect似乎是解决方法,所以我只是在bash脚本中“嵌入”了expect。 - adamm
@adamm 我认为我通过将 send_userexpect_user 更改为分别的 send_ttyexpect_tty 来修复了它。 我认为 stty 不需要更改。 - Donal Fellows
现在它可以工作了。虽然不是我在问题中一直在寻找的,但它非常有用。 - adamm

0

来自man sudo:sudoers策略会缓存凭据15分钟,除非在sudoers(5)中被覆盖。

因此,在调用expect之前,请使用sudo -v。它会检查凭据,如果它们没有被缓存,它将要求输入root密码。这样,您就可以在expect之前输入它们,以后的sudo命令就不需要再次询问了。


another_script.sh正在执行了几个小时,因此在此期间缓存的凭据已过期。 - adamm
@adamm,你能否在/etc/sudoers中添加相关命令以实现NOPASSWD访问? - Robert
我做不到。 - adamm

0

根据您的评论,我认为您已经将其放在cron中。在这种情况下,您需要以sudo身份运行脚本。您可以在sudo crontab中调用它。您可以通过运行sudo crontab -e来访问它。


在 cron 中运行时,您绝对不能与用户交谈,而必须从其他地方获取凭据。在所有选项中,从文件中读取是较好的选择。或者,您可以将脚本放在 root 的 crontab 中,根本不需要凭据... - Donal Fellows

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接