在使用ssh或者scp的时候都需要用户输入密码,可有些时候我们希望在脚本里面执行ssh/scp以免去用户输入密码的过程。
ssh/scp能不能从文件或者其他地方获取密码,而不需要用终端交互输入呢。
工具expect可以用来满足这种需求:
- 定义一个expect脚本文件
$ cat ./exp
#!/usr/bin/env expect
# Usage example:
# ssh: exp <mypassword> ssh <remoteuser>@<remotehost> <remotecommand>
# scp: exp <mypassword> scp <localfile> <remoteuser>@<remotehost>:<remotefile>
set timeout 20
set password [lindex $argv 0]
set cmd [lindex $argv 1]
set param [lrange $argv 2 end]
eval spawn $cmd -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $param
expect "password:"
send "$password\r";
interact
- 执行脚本文件
远程执行:
./exp <mypassword> ssh <remoteuser>@<remotehost> <remotecommand>
远程拷贝:
./exp <mypassword> scp <localfile> <remoteuser>@<remotehost>:<remotefile> # 本地文件拷远程
./exp <mypassword> scp <remoteuser>@<remotehost>:<remotefile> <localfile> # 远端文件拷本地
- 使用限制
上述exp不能使用在stdin发生重定向过的环境下面,例如:
$ cat t.data
aa
$ cat t.sh
#!/bin/bash
file=t.data
while IFS= read -r line; do
./exp mypassword ssh username@hostname ls
done < "$file"
运行结果是什么:
$ ./t.sh
spawn ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no username@hostname ls
Warning: Permanently added 'hostname,10.9.X.X' (ECDSA) to the list of known hosts.
username@hostname's password: [username@localhost ~/]$
expect无法从当前环境中识别出状态,也就无法给ssh输送密码域。
解决的办法是不要使用输入输出重定向,可以先把文件的内容读出到变量里面,然后再来循环变量,保证在循环的时候,也就是调用exp的时候没有重定向行为发生。