检查是否安装 tcl,expect
如果没有安装, 使用 yum 安装:
- yum install -y tcl
- yum install -y expect
expect 参数
启用选项
-c: 执行脚本前先执行的命令, 可多次使用.
-d:debug 模式, 可以在运行时输出一些诊断信息, 与在脚本开始处使用 exp_internal 1 相似.
-D: 启用交换调式器, 可设一整数参数.
-f: 从文件读取命令, 仅用于使用 #! 时. 如果文件名为 "-", 则从 stdin 读取(使用 "./-" 从文件名为 - 的文件读取).
-i: 交互式输入命令, 使用 "exit" 或 "EOF" 退出输入状态.
--: 标示选项结束(如果你需要传递与 expect 选项相似的参数给脚本时), 可放到 #! 行:
- #!/usr/bin/expect --
- .
-v: 显示 expect 版本信息.
expect 的 4 个命令
Expect 中最关键的四个命令是 send,expect,spawn,interact.
命令 | 说明 |
---|---|
send | 用于向进程发送字符串 |
expect | 从进程接收字符串 |
spawn | 启动新的进程 |
interact | 允许用户交互 |
常用命令
- `# 命令行参数 `
- `# $argv, 参数数组, 使用 [lindex $argv n] 获取,$argv 0 为脚本名字 `
- `# $argc, 参数个数 `
- `set` `username [lindex $argv 1]` `# 获取第 1 个参数 `
- `set` `passwd` `[lindex $argv 2]` `# 获取第 2 个参数 `
- `set` `timeout 30` `# 设置超时 `
- `# spawn 是 expect 内部命令, 开启 ssh 连接 `
- `spawn` `ssh` `-l username 192.168.1.1`
- `# 判断上次输出结果里是否包含 "password:" 的字符串, 如果有则立即返回, 否则就等待一段时间 (timeout) 后返回 `
- `expect` `"password:"`
- `# 发送内容 ispass(密码, 命令等)`
- `send` `"ispass\r"`
- `# 发送内容给用户 `
- `send_user` `"$argv0 [lrange $argv 0 2]\n"`
- `send_user` `"It's OK\r"`
- `# 执行完成后保持交互状态, 控制权交给控制台(手工操作). 否则会完成后会退出.`
- `interact`
命令介绍
close: 关闭当前进程的连接.
debug: 控制调试器.
disconnect: 断开进程连接(进程仍在后台运行).
定时读取密码, 执行 priv_prog
- `send_user` `"password?\"`
- `expect_user -re` `"(.*)\n"`
- `for` `{} 1 {} {`
- `if` `{[fork]!=0} {``sleep` `3600;``continue``}`
- `disconnect`
- `spawn priv_prog`
- `expect Password:`
- `send` `"$expect_out(1,string)\r"`
`. . .`
- `exit`
- `}`
exit: 退出 expect.
exp_continue [-continue_timer]: 继续执行下面的匹配.
exp_internal [-f file] value:
expect 范例
1. 远程登录并创建文件后退出
- #!/usr/bin/expect ## 注意路径, 使用 [whereis expect] 查看
- set user "hadoop" ## 设定参数, 注意 ",'的区别
- set pwd "yangkun"
- set host "48.93.36.144"
- set timeout -1 ##; 号可有可无
- spawn SSH -p 2020 $user@$host
- expect { ##expect 后有空格
- "*yes/no" {send "yes\r";exp_continue}
- "*password:" {send "$pwd\r"}
- }
- expect "]*" ## 通配符, 使用 ]* 有效, 使用 *# 无效
- send "touch /home/hadoop/aa.txt\r"
- expect "]*"
- send "echo hello world>> /home/hadoop/aa.txt\r"
- expect "]*"
- [interact] ## 人为交互
- send "exit\r" ## 退出
2. 配置免密登录并安装 JDK
- #!/bin/bash
- #!/usr/bin/expect
- SERVERS="114.114.114.114" ## 数组以空格分隔, 可以为目标 ip 或者 hostName
- PASSWORD="yangkun"
- ## 实现免密登录配置的函数
- auto_ssh_copy_id() {
- expect -c "set timeout -1;
- spawn SSH-copy-id \"-p 2020 $1\"; ## 这里要注意, 使用'或 \'不可行
- expect {
- *(yes/no)* {send -- yes\r;exp_continue;}
- *password:* {send -- $2\r;exp_continue;}
- eof {exit 0;}
- }";
- }
- ## 循环执行, 配置主机到从节点所有免密
- ssh_copy_id_to_all() {
- for SERVER in $SERVERS ## 取值需要加 $
- do
- auto_ssh_copy_id $SERVER $PASSWORD
- done
- }
- ## 调用循环配置函数
- ssh_copy_id_to_all
- ## 批量部署
- for SERVER in $SERVERS
- do
- scp install.sh root@$SERVER:/root
- SSH root@$SERVER /root/install.sh
- done
读取文件中的 host 配置
让脚本自动读取 slaves 文件中的机器名来批量安装
- cat slaves | while read host
- do
- echo $host
- expect -c "set timeout -f
- spawn SSH-copy-id $host"
- done
3. 批量配置 JDK,install.sh
- #!/bin/bash
- BASE_SERVER=master
- BASE_PATH=/home/hadoop/soft
- TARGET_PATH=/usr/local
- JAVA_PATH=$TARGET_PATH/java
- ## 1. 判断是否存在文件夹, 不存在则创建 soft 文件夹
- #if [ ! -d "$BASE_PATH" ]; then
- # mkdir "$BASE_PATH"
- #fi
- ## 2. 从指定 host 拷贝 jdk 到目标机器上(已经拷贝文件夹)
- scp -r $BASE_SERVER:$BASE_PATH $BASE_PATH
- ## 2. 解压 jdk 到指定目录
- if [ ! -d "$JAVA_PATH" ]; then
- sudo -S mkdir -p "$JAVA_PATH"
- fi
- ## 赋予权限
- sudo -S chmod -R hadoop:hadoop $JAVA_PATH
- tar -zxvf $BASE_PATH/jdk1.8.0_121.tar.gz -C $JAVA_PATH
- #### 3. 配置环境变量
- sudo -S cat>>/etc/profile<<EOF
- export JAVA_HOME=$JAVA_PATH/jdk1.8.0_121
- export PATH=\$PATH:\$JAVA_HOME/bin
- EOF
自动 telnet 会话
- #!/usr/bin/expect -f
- set ip [lindex $argv 0 ] # 接收第 1 个参数, 作为 IP
- set userid [lindex $argv 1 ] # 接收第 2 个参数, 作为 userid
- set mypassword [lindex $argv 2 ] # 接收第 3 个参数, 作为密码
- set mycommand [lindex $argv 3 ] # 接收第 4 个参数, 作为命令
- set timeout 10 # 设置超时时间
- # 向远程服务器请求打开一个 telnet 会话, 并等待服务器询问用户名
- spawn telnet $ip
- expect "username:"
- # 输入用户名, 并等待服务器询问密码
- send "$userid\r"
- expect "password:"
- # 输入密码, 并等待键入需要运行的命令
- send "$mypassword\r"
- expect "%"
- # 输入预先定好的密码, 等待运行结果
- send "$mycommand\r"
- expect "%"
- # 将运行结果存入到变量中, 显示出来或者写到磁盘中
- set results $expect_out(buffer)
- # 退出 telnet 会话, 等待服务器的退出提示 EOF
- send "exit\r"
- expect eof
4. 自动建立 FTP 会话
- #!/usr/bin/expect -f
- set ip [lindex $argv 0 ] # 接收第 1 个参数, 作为 IP
- set userid [lindex $argv 1 ] # 接收第 2 个参数, 作为 Userid
- set mypassword [lindex $argv 2 ] # 接收第 3 个参数, 作为密码
- set timeout 10 # 设置超时时间
- # 向远程服务器请求打开一个 FTP 会话, 并等待服务器询问用户名
- spawn ftp $ip
- expect "username:"
- # 输入用户名, 并等待服务器询问密码
- send "$userid\r"
- expect "password:"
- # 输入密码, 并等待 FTP 提示符的出现
- send "$mypassword\r"
- expect "ftp>"
- # 切换到二进制模式, 并等待 FTP 提示符的出现
- send "bin\r"
- expect "ftp>"
- # 关闭 ftp 的提示符
- send "prompt\r"
- expect "ftp>"
- # 下载所有文件
- send "mget *\r"
- expect "ftp>"
- # 退出此次 ftp 会话, 并等待服务器的退出提示 EOF
- send "bye\r"
- expect eof
自动登录 SSH 执行命令
- #!/usr/bin/expect
- set IP [lindex $argv 0]
- set USER [lindex $argv 1]
- set PASSWD [lindex $argv 2]
- set CMD [lindex $argv 3]
- spawn SSH $USER@$IP $CMD
- expect {
- "(yes/no)?" {
- send "yes\r"
- expect "password:"
- send "$PASSWD\r"
- }
- "password:" {send "$PASSWD\r"}
- "* to host" {exit 1}
- }
- expect eof
5. 自动登录 SSH
- #!/usr/bin/expect -f
- set ip [lindex $argv 0 ] # 接收第 1 个参数, 作为 IP
- set username [lindex $argv 1 ] # 接收第 2 个参数, 作为 username
- set mypassword [lindex $argv 2 ] # 接收第 3 个参数, 作为密码
- set timeout 10 # 设置超时时间
- spawn SSH $username@$ip # 发送 SSH 请求
- expect {
- # 返回信息匹配
- "*yes/no" {
- send "yes\r"; exp_continue
- } # 第一次 SSH 连接会提示 yes/no, 继续
- "*password:" {
- send "$mypassword\r"
- } # 出现密码提示, 发送密码
- }
- interact # 交互模式, 用户会停留在远程服务器上面
6. 批量登录 SSH 服务器执行操作范例, 设定增量的 for 循环
- #!/usr/bin/expect
- for {set i 10} {$i <= 12} {incr i} {
- set timeout 30
- set ssh_user [lindex $argv 0]
- spawn SSH -i .SSH/$ssh_user abc$i.com
- expect_before "no)?" {
- send "yes\r" }
- sleep 1
- expect "password*"
- send "hello\r"
- expect "*#"
- send "echo hello expect!> /tmp/expect.txt\r"
- expect "*#"
- send "echo\r"
- }
- exit
7. 批量登录 SSH 并执行命令, foreach 语法
- #!/usr/bin/expect
- if {$argc!=2} {
- send_user "usage: ./expect ssh_user password\n"
- exit
- }
- foreach i {11 12} {
- set timeout 30
- set ssh_user [lindex $argv 0]
- set password [lindex $argv 1]
- spawn SSH -i .SSH/$ssh_user root@xxx.yy.com
- expect_before "no)?" {
- send "yes\r" }
- sleep 1
- expect "Enter passphrase for key*"
- send "password\r"
- expect "*#"
- send "echo hello expect!> /tmp/expect.txt\r"
- expect "*#"
- send "echo\r"
- }
- exit
8. 另一自动 SSH 范例, 从命令行获取服务器 IP,foreach 语法, expect 嵌套
- #!/usr/bin/expect
- # 使用方法: script_name ip1 ip2 ip3 ...
- set timeout 20
- if {$argc <1} {
- puts "Usage: script IPs"
- exit 1
- }
- # 替换你自己的用户名
- set user "username"
- # 替换你自己的登录密码
- set password "yourpassword"
- foreach IP $argv {
- spawn SSH $user@$IP
- expect \
- "(yes/no)?" {
- send "yes\r"
- expect "password:?" {
- send "$password\r"
- }
- } "password:?" {
- send "$password\r"
- }
- expect "\$?"
- # 替换你要执行的命令
- send "last\r"
- expect "\$?"
- sleep 10
- send "exit\r"
- expect eof
- }
9. 批量 SSH 执行命令, 用 shell 调用 tclsh 方式, 多进程同时执行
- * tclsh - Simple shell containing Tcl interpreter
- #!/bin/sh
- # -*- tcl -*- \
- exec tclsh $0 "$@"
- package require Expect
- set username [lindex $argv 0]
- set password [lindex $argv 1]
- set argv [lrange $argv 2 end]
- set prompt "(%|#|\\$) $"
- foreach ip $argv {
- spawn SSH -t $username@$ip sh
- lappend ids $spawn_id
- }
- expect_before -i ids eof {
- set index [lsearch $ids $expect_out(spawn_id)]
- set ids [lreplace $ids $index $index]
- if [llength $ids] exp_continue
- }
- expect -i ids "(yes/no)\\?" {
- send -i $expect_out(spawn_id) yes\r
- exp_continue
- } -i ids "Enter passphrase for key" {
- send -i $expect_out(spawn_id) \r
- exp_continue
- } -i ids "assword:" {
- send -i $expect_out(spawn_id) $password\r
- exp_continue
- } -i ids -re $prompt {
- set spawn_id $expect_out(spawn_id)
- send "echo hello; exit\r"
- exp_continue
- } timeout {
- exit 1
- }
10.SSH 登录过程常规提示文字
- The authenticity of host '192.168.17.35 (192.168.17.35)' can't be established.
- RSA key fingerprint is 25:e8:4c:89:a3:b2:06:ee:de:66:c7:7e:1b:fa:1c:c5.
- Are you sure you want to continue connecting (yes/no)?
- Warning: Permanently added '192.168.17.35' (RSA) to the list of known hosts.
- Enter passphrase for key '/data/key/my_dsa':
- Last login: Sun Jan 26 13:39:37 2014 from 192.168.11.143
- [root@master003 ~]#
- root@192.168.16.90's password:
- Last login: Thu Jan 23 17:50:43 2014 from 192.168.11.102
- [root@lvsmaster ~]#
11.SSH 自动登录 expect 脚本: SSH.expect
- #!/usr/bin/expect -f
- # Auther:YuanXing
- # Update:2014-02-08
- if {$argc < 4} {
- send_user "Usage:\n $argv0 IPaddr User Passwd Port Passphrase\n"
- puts stderr "argv error!\n"
- sleep 1
- exit 1
- }
- set ip [lindex $argv 0 ]
- set user [lindex $argv 1 ]
- set passwd [lindex $argv 2 ]
- set port [lindex $argv 3 ]
- set passphrase [lindex $argv 4 ]
- set timeout 6
- if {$port == ""} {
- set port 22
- }
- #send_user "IP:$ip,User:$user,Passwd:$passwd,Port:$port,Passphrase:$passphrase"
- spawn SSH -p $port $user@$ip
- expect_before "(yes/no)\\?" {
- send "yes\r"}
- expect \
- "Enter passphrase for key*" {
- send "$passphrase\r"
- exp_continue
- } "password:?" {
- send "$passwd\r"
- exp_continue
- } "*\[#\\\$]" {
- interact
- } "* to host" {
- send_user "Connect faild!"
- exit 2
- } timeout {
- send_user "Connect timeout!"
- exit 2
- } eof {
- send_user "Lost connect!"
- exit
- }
- 12.Mikrotik backup script using SSH and expect
- #!/bin/bash
- # TAG: mikrotik, SSH, expect, lftp
- BACKUP_DIR="/var/backups"
- HOSTNAME="192.168.88.1"
- PORT="22"
- USER="admin"
- PASS="123456"
- TMP=$(mktemp)
- TODAY=$(date +%F)
- FILENAME="$HOSTNAME-$TODAY"
- PATH="/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin"
- # create expect script
- cat> $TMP <<EOF
- #exp_internal 1 # Uncomment for debug
- set timeout -1
- spawn SSH -p$PORT $USER@$HOSTNAME
- match_max 100000
- expect -exact "password:"
- send -- "$PASS\r"
- sleep 1
- expect ">"
- send -- "/export file=$FILENAME\r"
- expect ">"
- send -- "/system backup save name=$FILENAME\r"
- expect ">"
- send -- "quit\r"
- expect eof
- EOF
- # run expect script
- #cat $TMP # Uncomment for debug
- expect -f $TMP
- # remove expect script
- rm $TMP
- # download and remove backup files
- # "xfer:clobber on" means overwrite existing files
- cd ${BACKUP_DIR}
- echo "
- set xfer:clobber on
- get ${FILENAME}.rsc
- rm ${FILENAME}.rsc
- get ${FILENAME}.backup
- rm ${FILENAME}.backup" |
- lftp -u $USER,$PASS $HOSTNAME
来源: https://yq.aliyun.com/articles/701512