无法通过 expect 对 bash 命令的脚本进行 SSH
Unable to SSH a script of bash commands via expect
我正在尝试将一组命令推送到多个远程主机。我可以根据个人情况使用以下内容:
ssh user@remoteHost "bash -s" <./commands.sh
我可以把它放到一个循环中,但是我不得不输入 n 次密码。在 SSH 配置文件中禁用密码提示对我来说不是一个选项。
我试图在循环中使用 expect,但我无法让它工作。
#!/bin/bash
HOSTS="host1 host2"
read -sp "Password: " PASSWORD
for HOST in $HOSTS; do
expect -c "
spawn /usr/bin/ssh user@$HOST "bash -s" <./commands.sh
expect_before {
"*yes/no*" {send "yes"\r;exp_continue}}
expect {
"*Password*" {send $PASSWORD\r;interact}}
exit"
done
我收到以下错误:
spawn /usr/bin/ssh root@host1 bash
expect: invalid option -- 's'
usage: expect [-div] [-c cmds] [[-f] cmdfile] [args]
spawn /usr/bin/ssh root@host2 bash
expect: invalid option -- 's'
usage: expect [-div] [-c cmds] [[-f] cmdfile] [args]
有什么想法吗?似乎 expect 正在尝试解释 bash 命令。我不确定如何阻止它。
解决方案:
替换
spawn /usr/bin/ssh user@$HOST "bash -s" <./commands.sh
与
spawn sh -c {ssh root@$HOST 'bash -ls' < /tmp/commands.sh}
最终代码:
#!/bin/bash
HOSTS="host1 host2"
read -sp "Password: " PASSWORD
for HOST in $HOSTS; do
expect -c "
spawn sh -c {ssh root@$HOST 'bash -ls' < /tmp/commands.sh}
expect_before {
"*yes/no*" {send "yes"\r;exp_continue}}
expect {
"*assword*" {send $PASSWORD\r;interact}}
exit"
done
我建议这样做:
#!/bin/bash
hosts=(host1 host2)
read -sp "Password: " password
for host in "${hosts[@]}"; do
env h="$host" p="$password" expect <<'END_EXPECT'
spawn sh -c "/usr/bin/ssh user@$env(h) 'bash -s' <./commands.sh"
expect {
"*yes/no*" {send "yes\r"; exp_continue}
"*Password*" {send "$env(p)\r"}
}
interact
END_EXPECT
done
笔记
- 使用小写变量名:保留大写变量名供 shell 使用
- 使用引用的 heredoc 来包含预期代码
- 这让您可以在 expect 中使用单引号和双引号,而不必担心 shell
中的引号地狱
- 使用
env
通过环境传递shell变量
- 简化您的期望语句
使用
的危险
expect -c " ...; expect "*Password*" ..."
是内部双引号与外部引号匹配,并被 shell 删除。这使得 *Password*
成为一个裸露的 glob,shell 可以根据当前目录中的文件和 shell 设置进行扩展。例如,创建一个名为 "The Password" 的文件(带有 space),您将得到一个
错误。
我正在尝试将一组命令推送到多个远程主机。我可以根据个人情况使用以下内容:
ssh user@remoteHost "bash -s" <./commands.sh
我可以把它放到一个循环中,但是我不得不输入 n 次密码。在 SSH 配置文件中禁用密码提示对我来说不是一个选项。
我试图在循环中使用 expect,但我无法让它工作。
#!/bin/bash
HOSTS="host1 host2"
read -sp "Password: " PASSWORD
for HOST in $HOSTS; do
expect -c "
spawn /usr/bin/ssh user@$HOST "bash -s" <./commands.sh
expect_before {
"*yes/no*" {send "yes"\r;exp_continue}}
expect {
"*Password*" {send $PASSWORD\r;interact}}
exit"
done
我收到以下错误:
spawn /usr/bin/ssh root@host1 bash
expect: invalid option -- 's'
usage: expect [-div] [-c cmds] [[-f] cmdfile] [args]
spawn /usr/bin/ssh root@host2 bash
expect: invalid option -- 's'
usage: expect [-div] [-c cmds] [[-f] cmdfile] [args]
有什么想法吗?似乎 expect 正在尝试解释 bash 命令。我不确定如何阻止它。
解决方案:
替换
spawn /usr/bin/ssh user@$HOST "bash -s" <./commands.sh
与
spawn sh -c {ssh root@$HOST 'bash -ls' < /tmp/commands.sh}
最终代码:
#!/bin/bash
HOSTS="host1 host2"
read -sp "Password: " PASSWORD
for HOST in $HOSTS; do
expect -c "
spawn sh -c {ssh root@$HOST 'bash -ls' < /tmp/commands.sh}
expect_before {
"*yes/no*" {send "yes"\r;exp_continue}}
expect {
"*assword*" {send $PASSWORD\r;interact}}
exit"
done
我建议这样做:
#!/bin/bash
hosts=(host1 host2)
read -sp "Password: " password
for host in "${hosts[@]}"; do
env h="$host" p="$password" expect <<'END_EXPECT'
spawn sh -c "/usr/bin/ssh user@$env(h) 'bash -s' <./commands.sh"
expect {
"*yes/no*" {send "yes\r"; exp_continue}
"*Password*" {send "$env(p)\r"}
}
interact
END_EXPECT
done
笔记
- 使用小写变量名:保留大写变量名供 shell 使用
- 使用引用的 heredoc 来包含预期代码
- 这让您可以在 expect 中使用单引号和双引号,而不必担心 shell 中的引号地狱
- 使用
env
通过环境传递shell变量 - 简化您的期望语句
使用
的危险expect -c " ...; expect "*Password*" ..."
是内部双引号与外部引号匹配,并被 shell 删除。这使得 *Password*
成为一个裸露的 glob,shell 可以根据当前目录中的文件和 shell 设置进行扩展。例如,创建一个名为 "The Password" 的文件(带有 space),您将得到一个
错误。