1. 程式人生 > >expect-調試模式的使用

expect-調試模式的使用

display ins send 模式匹配 邏輯 string 整數 cells 正常

一、expect簡介

Expect是一種TCL擴展性的語言,主要用於完成系統交互方面的功能,比如SSH、FTP等,這些程序都需要手工與它們進行互動,而使用Expect就可以模擬人手工互動的過程,是一種自動的方式控制。

在使用shell編寫巡檢腳本的過程中,常使用expect工具與巡檢機器進行交互。通常我們在碰到交互邏輯無法往下進行的時候,這就需要使用到調試模式來具體分析。

二、安裝

配置好yum後,安裝命令如下

yum install expect

三、從一個簡單的腳本開始

  1 #!/usr/bin/expect -d
  2 #writed by ahao
  3 #註意,在spawn中,如果要註釋的話,另外起一行,放在代碼的後面會有問題,這點與bash不一樣
4 set timeout 10 5 spawn -noecho ssh -o StrictHostKeyChecking=no -l test 192.168.2.151 -p 22 6 #spawn命令是expect的初始命令,他用於啟動一個進程,之後所有操作都在這個進程中進行,如果沒有spawn,這個expect都無法進行 7 #StrictHostKeyChecking=no參數讓ssh默認添加新主機的公鑰指紋,也就不會出現出現是否繼續yes/no的提示了 8 expect "password:" {send "123456\r"} 9 expect "Last login
" {send "echo test1\r"} #執行命令 10 expect "*\$*" {send "echo test2\r"} #執行命令 11 expect eof 12 #EOF(End Of File),表示"文字流"(stream)的結尾。這裏的"文字流",可以是文件(file),也可以是標準輸入(stdin),EOF是不可輸出字符,因此不能在屏幕上顯示。由於字符的ASCII碼不可能出現-1,因此EOF定義為-1是合適的。當讀入的字符值等於EOF時,表示讀入的已不是正常的字符而是文件結束符,但這適用對文本文件的讀寫 13 #也就是說eof通常定義為-1,在本文文件中數據都是以字符的ASCII代碼值的形式存放。我們知道,ASCII代碼值的範圍是0~255,不可能出現-1,因此可以用EOF作為文件結束標誌,如果處理時返回-1的值,那就eof

註:腳本中有4個expect過程

  • 第一個expect:匹配到輸入密碼的提示,輸入密碼
  • 第二個expect:輸入密碼後會有上次登錄的一個信息,匹配”Last login”後執行一個echo命令
  • 第三個expect:執行echo命令完成後,匹配提示符”$”後再執行一個echo命令
  • 第四個expect命令:匹配eof

整個執行過程如下:1-3expect過程分別使用不同顏色標識

[patrol@report-server db_bin]$ ./auto_login.sh
expect version 5.44.1.15
argv[0] = /usr/bin/expect argv[1] = -d argv[2] = ./auto_login.sh
set argc 0
set argv0 "./auto_login.sh"
set argv ""
executing commands from command file ./auto_login.sh
parent: waiting for sync byte
parent: telling child to go ahead
parent: now unsynchronized from child
spawn: returns {121224}

expect: does "" (spawn_id exp4) match glob pattern "password:"? no
[email protected]‘s password:
expect: does "[email protected]‘s password: " (spawn_id exp4) match glob pattern "password:"? yes
expect: set expect_out(0,string) "password:"
expect: set expect_out(spawn_id) "exp4"
expect: set expect_out(buffer) "[email protected]‘s password:"
send: sending "123456\r" to { exp4 }

expect: does " " (spawn_id exp4) match glob pattern "Last login"? no


expect: does " \r\n" (spawn_id exp4) match glob pattern "Last login"? no
Last login: Sun Nov 25 20:35:43 2018 from 192.168.2.150

expect: does " \r\nLast login: Sun Nov 25 20:35:43 2018 from 192.168.2.150\r\r\n" (spawn_id exp4) match glob pattern "Last login"? yes
expect: set expect_out(0,string) "Last login"
expect: set expect_out(spawn_id) "exp4"
expect: set expect_out(buffer) " \r\nLast login"
send: sending "echo test1\r" to { exp4 }

expect: does ": Sun Nov 25 20:35:43 2018 from 192.168.2.150\r\r\n" (spawn_id exp4) match glob pattern "*$*"? no
echo test1

expect: does ": Sun Nov 25 20:35:43 2018 from 192.168.2.150\r\r\necho test1\r\n" (spawn_id exp4) match glob pattern "*$*"? no
[test@report-server-backup ~]$ echo test1
test1
[test@report-server-backup ~]$
expect: does ": Sun Nov 25 20:35:43 2018 from 192.168.2.150\r\r\necho test1\r\n[test@report-server-backup ~]$ echo test1\r\ntest1\r\n[test@report-server-backup ~]$ " (spawn_id exp4) match glob pattern "*$*"? yes
expect: set expect_out(0,string) ": Sun Nov 25 20:35:43 2018 from 192.168.2.150\r\r\necho test1\r\n[test@report-server-backup ~]$ echo test1\r\ntest1\r\n[test@report-server-backup ~]$ "
expect: set expect_out(spawn_id) "exp4"
expect: set expect_out(buffer) ": Sun Nov 25 20:35:43 2018 from 192.168.2.150\r\r\necho test1\r\n[test@report-server-backup ~]$ echo test1\r\ntest1\r\n[test@report-server-backup ~]$ "
send: sending "echo test2\r" to { exp4 }

echo test2
test2
[test@report-server-backup ~]$ expect: timed out
[patrol@report-server db_bin]$

為了對比分析,把調試模式關閉,整個輸出如下

[patrol@report-server db_bin]$ ./auto_login.sh
[email protected]‘s password:
Last login: Sun Nov 25 20:38:03 2018 from 192.168.2.150
echo test1
[test@report-server-backup ~]$ echo test1
test1
[test@report-server-backup ~]$ echo test2
test2

四、過程分析

首先了解一下expect_out數組,expect_out數組專用與expect命令,裏面的元素包括:

expect_out(buffer)、expect_out(X,string)、expect_out(X,start)、expect_out(X,end)、expect_out(spawn_id)

上面的X字符表示從0-9的整數,具體如下

  • expect_out(buffer)是一個看起來比較特殊的變量,這個變量中放置從上一次匹配到這一次匹配之間的所有返回,包括匹配字符本身。----重點
  • expect_out(x,string):x是一個數字,從0到9,加起來一共是10個變量,其中0變量比較特殊,它裏面保存整個expect正則表達式中匹配到的所有內容,而從1開始到9的變量中,保存正則表達式中子模式的值,也就是括在()中的那部分內容,從左往右計算
  • expect_out(x,start|end):x與上面的含義相同,這個變量的值是一個數值,以expect_out(1,start)為例,它裏面保存第一個子模式在expect_out(buffer)變量中匹配到的時候的索引值,比如第一個子模式匹配是在expect_out(buffer)中的第10個字符開始匹配到的,那麽這個變量的值就是10

結合上面的的第1個expect過程進行解析:

  1 expect: does "" (spawn_id exp4) match glob pattern "password:"? no   #不匹配,expect: does執行匹配動作,包括源字符串與需匹配的字符串,匹配是否成功,如果匹配成功,下面輸出相關信息與動作,這裏“ match glob pattern”匹配的是通配符表達式,如果使用正則表達式,會是類似如下輸出:match regular expression ".*#"?,此時expect_out(buffer)為空
  2  [email protected]‘s password:                   #輸出
  3  expect: does "[email protected]‘s password: " (spawn_id exp4) match glob pattern "password:"? yes   #yes代表匹配
  4  expect: set expect_out(0,string) "password:"           #匹配到的字符串為”password
  5  expect: set expect_out(spawn_id) "exp4"             #spawn_id進程為exp4
  6  expect: set expect_out(buffer) "[email protected]‘s password:"   #expect_out(buffer)變量的信息:[email protected]‘s password:
  7  send: sending "123456\r" to { exp4 }  #執行的動作,發送字符串到exp4

結合上面的第2個expect過程進行解析:

  1 
  2  expect: does " \r\n" (spawn_id exp4) match glob pattern "Last login"? no  #expect_out(buffer)變量為\r\n,在進行第2次expect的時候,expect_out(buffer)變量是不包含第一次匹配的字符串的
  3  Last login: Sun Nov 25 20:35:43 2018 from 192.168.2.150    #上一個expect_send後的輸出
  4 
  5 expect: does " \r\nLast login: Sun Nov 25 20:35:43 2018 from 192.168.2.150\r\r\n" (spawn_id exp4) match glob pattern "Last login"? yes               #匹配了,下面開始相關信息的輸出與執行下一步的動作
  6  expect: set expect_out(0,string) "Last login"   #匹配到的字符串
  7  expect: set expect_out(spawn_id) "exp4"
  8  expect: set expect_out(buffer) " \r\nLast login"  #expect_out(buffer)變量的值為“\r\nLast login”
  9  send: sending "echo test1\r" to { exp4 }  #發送命令到exp4

第3個expect分析:略

五、提一個問題

第二個expect和第三個expect順序調轉下,會是啥結果?如下代碼:

  1 #!/usr/bin/expect
  2 #writed by ahao
  3 #註意,在spawn中,如果要註釋的話,另外起一行,放在代碼的後面會有問題,這點與bash不一樣
  4 set timeout 10
  5 spawn -noecho ssh -o StrictHostKeyChecking=no -l test 192.168.2.151 -p 22
  6 #spawn命令是expect的初始命令,他用於啟動一個進程,之後所有操作都在這個進程中進行,如果沒有spawn,這個expect都無法進行
  7 #StrictHostKeyChecking=no參數讓ssh默認添加新主機的公鑰指紋,也就不會出現出現是否繼續yes/no的提示了
  8 expect "password:" {send "123456\r"}
  9 expect "*\$*" {send "echo test2\r"}
 10 expect "Last login" {send "echo test1\r"}
 11 expect eof
 12 #EOF(End Of File),表示"文字流"(stream)的結尾。這裏的"文字流",可以是文件(file),也可以是標準輸入(stdin),EOF是不可輸出字符,因此不能在屏幕上顯示。由於字符的ASCII碼不可能出現-1,因此EOF定義為-1是合適的。當讀入的字符值等於EOF時,表示讀入的已不是正常的字符而是文件結束符,但這適用對文本文件的讀寫
 13 #也就是說eof通常定義為-1,在本文文件中數據都是以字符的ASCII代碼值的形式存放。我們知道,ASCII代碼值的範圍是0~255,不可能出現-1,因此可以用EOF作為文件結束標誌,如果處理時返回-1的值,那就eof

結果就是:第三個expect不可能匹配成功, expect_out(buffer)變量已不可能包含關鍵字”Last login

技術分享圖片

expect-調試模式的使用