shell編程之Expect免交互

shell編程之Expect免交互

一、前言

​ shell腳本存在的核心意義就在於基於shell命令簡化甚至省略可避免的人工操作,通過各種控制流程結構以及正則表達式等方法,逐步實現自動化操作的整個過程,由此也可見,shell並沒有面向對象的思想,類似C語言,畢竟C語言是操作系統或者說是內核的核心語言。

​ 所以,語言並無優劣之分,只是每個人使用的習慣與方式不同,換句話說,難易的不是語言,而是思想與突如其來的靈感。

二、Expect概述與安裝

Expect概述

​ Expect是建立在TCL基礎上的一個工具,Expect是用來進行自動化控制和測試的工具。主要解決shell腳本中不可交互的問題。對於大規模的Linux運維很有幫助。

​ 在Linux運維和開發中,我們經常需要遠程登錄服務器進行操作,登錄的過程是一個交互過程,需要輸入yes/no password等信息。爲了模擬這種輸入,可以使用Expect腳本。

Expect的安裝:yum install -y expect

三、基本命令

  1. send:向進程發送字符串,用於模擬用戶的輸入,但不支持換行 一般需要加上 \r

  2. expect:內部命令

    ​ 判斷上次輸出結果裏是否包含指定的字符串,有則返回,反之就等待超時時間後返回。

    ​ 只能捕捉由spawn啓動的進程的輸出。

  3. spawn:啓動進程,並跟蹤後續交互信息。

  4. interact:執行完成後保持交互狀態,把控制權交給控制檯。

Timeout:指定超時時間,過期則繼續執行後續指令

  • 單位是s
  • timeout -1爲永不超時
  • 默認情況下timeout 是10s

exp_continue——允許expect繼續向下執行指令(比較關鍵,多次交互)

send_user——回顯命令,相當於echo

四、expect語法

expect [選項] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]
選項
-c:從命令行執行expect腳本,默認expect是交互地執行的
示例:expect -c 'expect "\n" {send "pressed enter\n"}
-d:可以輸出輸出調試信息
示例:expect -d ssh.exp

expect最常用的語法(tcl語言:模式-動作)
單一分支模式語法:
expect “hi” {send “You said hi\n"} 匹配到hi後,會輸出“you said hi”,並換行,也可以使用\r
多分支模式語法:
expect "hi" { send "You said hi\n" } \ "hehe" { send “Hehe yourself\n" } \ "bye" { send “Good bye\n" }
匹配hi,hello,bye任意字符串時,執行相應輸出.等同如下:
expect { "hi" { send "You said hi\n"} "hehe" { send "Hehe yourself\n"} "bye" { send “Good bye\n"} }

五、實例:

1)ssh免交互遠程登錄

[root@lokott ~]# yum install -y expect
已加載插件:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
...//省略部分
[root@lokott shell]# cat a.sh 
#使用which expect查看其位置
#!/usr/bin/expect
#設置超時時間
    set timeout 20
    log_file test.log
    log_user 1
#變量定義
    set hostname [lindex $argv 0]
    set passwd [lindex $argv 1]
#啓動進程,spawn監控
    spawn ssh root@$hostname
#匹配條件
    expect {
        "(yes/no)"
#exp_continue表示繼續向下匹配
        {send "yes\r";exp_continue}  
        "*password"
        {send "$passwd\r"}
}
#轉交權限給控制檯
interact 

[root@lokott shell]# ./a.sh 192.168.68.129 123456   //第一次登錄
spawn ssh [email protected]
The authenticity of host '192.168.68.129 (192.168.68.129)' can't be established.
ECDSA key fingerprint is SHA256:k/6W9M/dgxVrbMgSx9nIFPGfVgUfLMoIb27ys9ZF+LM.
ECDSA key fingerprint is MD5:26:dd:06:b3:32:bd:d6:a3:2f:7c:66:7d:b9:c0:4b:c4.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.68.129' (ECDSA) to the list of known hosts.
[email protected]'s password: 
Last login: Wed Dec  4 10:24:33 2019 from 192.168.68.1
[root@localhost ~]# exit
登出
Connection to 192.168.68.129 closed.
[root@lokott shell]# ./a.sh 192.168.68.129 123456   //第二次登錄上一次登錄需要的祕鑰已經保存
spawn ssh [email protected]
[email protected]'s password: 
Last login: Wed Dec  4 10:29:42 2019 from 192.168.68.130
[root@localhost ~]# exit
登出
Connection to 192.168.68.129 closed.

上述演示的是直接使用expect腳本實現ssh遠程登錄的操作過程,而expect腳本也可以被嵌入到普通的腳本文件中,下面給出具體演示:

[root@lokott shell]# cat b.sh 
#嵌入expect執行實現免交互登錄
#!/bin/bash
#定義普通變量,在該shell腳本中始終有效
hostname=$1
passwd=$2
#嵌入寫入expect腳本內容??實現免交互的具體內容
/usr/bin/expect<<-EOF
spawn ssh root@$hostname
expect {
    "(yes/no)"
    {send "yes\r";exp_continue}
    "*password"
    {send "$passwd\r";}
}
#下兩行是爲了返回本地控制檯的操作演示
expect "*]#"
send "exit\r"
expect eof
EOF
#注意EOF的前後都不可以有空格!!!
[root@lokott shell]# ./b.sh 192.168.68.129 123456
spawn ssh [email protected]
[email protected]'s password: 
Last login: Wed Dec  4 12:22:23 2019 from 192.168.68.130
[root@localhost ~]# exit
登出
Connection to 192.168.68.129 closed.

推薦使用這種方式,因爲在實際使用中一般都是在普通腳本中使用expect的,所有嵌入寫入比較方便,比較常用!

直接使用expect免交互的完整腳本如下,有興趣可以試着改爲嵌入寫入的代碼:

[root@lokott shell]# cat ssh.sh 
#!/usr/bin/expect
set timeout 5
set hostname [lindex $argv 0]
set password [lindex $argv 1]
spawn ssh root@$hostname
expect {
"No route to host" exit
"Invalid argument" exit
"Connection refused" exit
"Name or service not known" exit
"to continue" {send "yes\r";exp_continue}
"password:" {send "$password\r"}
}
interact
exit
[root@lokott shell]# ./ssh.sh 192.168.68.133 123456  //服務器非在線
spawn ssh [email protected]
ssh: connect to host 192.168.68.133 port 22: No route to host
[root@lokott shell]# ./ssh.sh  123456    //參數輸入錯誤
spawn ssh root@123456
ssh: connect to host 123456 port 22: Invalid argument
[root@lokott shell]# ./ssh.sh 192.168.68.129 123456   //正常登錄
spawn ssh [email protected]
[email protected]'s password: 
Last login: Wed Dec  4 15:20:51 2019 from 192.168.68.130
[root@localhost ~]# 

2)添加用戶與用戶密碼的免交互操作過程

[root@lokott shell]# cat c.sh 
#!/bin/bash
username=$1
password=$2
useradd $1
[ $? -eq 0 ]&&echo ||exit 1
/usr/bin/expect<<-EOF
spawn passwd ${username}
expect {
    "密碼:"
    {send  "${password}\r";exp_continue}

    "密碼:"
    {send  "${password}\r";}

}
EOF

tail -1 /etc/shadow|awk -F: '{print $1,$2}'
userdel -r $1
#執行結果如下
[root@lokott shell]# ./c.sh zhazhahui 123456

spawn passwd zhazhahui
更改用戶 zhazhahui 的密碼 。
新的 密碼:
無效的密碼: 密碼少於 8 個字符
重新輸入新的 密碼:
passwd:所有的身份驗證令牌已經成功更新。
zhazhahui $6$k.MdzCd3$XMUGf5XQV4sE5RJqcwdEoOkd8UVgWtwcguzi6Md5yXcagYvgyE8GmCjzjxkEB5rXR.IRG9j49c36amzkDIe5l1

六、總結

​ 本文主要介紹了expect實現免交互的作用以及結合實際案例講解其兩種使用方式(直接和嵌入)的具體操作過程。

​ 過程主要分爲:基本變量定義(timeout最好最先設置)——spawn啓動跟蹤——expect匹配——interact交付控制權(沒有會退出)或者expect eof 結束expect匹配。

​ 務必結合上述的案例自己多使用體會理解!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章