1. 程式人生 > 實用技巧 >第九章 expect 和grep命令

第九章 expect 和grep命令

一、expect介紹

expect 是一個免費的程式設計工具,用來實現自動的互動式任務,而無需人為干預。說白了, expect
就是一套用來實現自動互動功能的軟體。需要安裝

yum install -y expect

expect基礎
在使用 expect 時,基本上都是和以下四個命令打交道:
命令 作用
spawn 啟動新的程序
expect 從程序接收字串
send 用於向程序傳送字串
interact 允許使用者互動
1.spawn 命令用來啟動新的程序, spawn 後的 expect 和 send 命令都是和使用 spawn 啟動的新程序進行互動。
2.expect 通常用來等待一個程序的反饋,我們根據程序的反饋,再使用 send 命令傳送對應的互動
命令。
3.send 命令接收一個字串引數,並將該引數傳送到程序。
4.interact 命令用的其實不是很多,一般情況下使用 spawn 、 expect 和 send 和命令就可以很好的完成我們的任務;但在一些特殊場合下還是需要使用 interact 命令的, interact 命令主要用
於退出自動化,進入人工互動。比如我們使用 spawn 、 send 和 expect 命令完成了ftp登陸主機,執行下載檔案任務,但是我們希望在檔案下載結束以後,仍然可以停留在ftp命令列狀態,以便手
動的執行後續命令,此時使用 interact 命令就可以很好的完成這個任務。

1)expect自動應答的基本步驟

第一步: 執行一個程式或命令=> spawn 命令資訊
第二步: 識別產生資訊關鍵字=> expect 捕獲關鍵字 {send 應答資訊}
第三步: 根據識別關鍵做處理=> send 應答資訊

二、expect例項

1)自動應答指令碼

#!/usr/bin/expect
spawn ssh [email protected] uptime
expect "yes/no"
send "yes\n"
expect "*assword"
send "1\n"
expect eof

2)解釋

#1、#!/usr/bin/expect -f:使用expect來解釋該指令碼
#2、spwan:
spawn是進入expect環境後才可以執行的expect內部命令,如果沒有裝expect或者直接在預設的SHELL下-執行是找不到spawn命令的。它主要的功能是給ssh執行程序加個殼,用來傳遞互動指令;
#3、expect:
expect "*assword":這裡的expect也是expect的一個內部命令,這個命令的意思是判斷上次輸出結果裡是否包含“password”的字串,如果有則立即返回;否則就等待一段時間後返回,這裡等待時長就是前面設定的30秒;
#4、send:
send "1\n":當匹配到對應的輸出結果時,就傳送密碼到開啟的ssh程序,執行互動動作;
首次登陸之後,再次登陸,就不會出現yes/no的提示了,所以上述指令碼再次執行會出現spawn 命令出
現互動式提問的expect 匹配不上的情況,此時指令碼會阻塞在原地,我們可以set timeout 3設定超時時間,單位為秒,預設情況下是10秒,以保障指令碼超時則結束,

#!/usr/bin/expect -f
spawn ssh [email protected] uptime
set timeout 3 # 某一條expect語句在原地匹配,超過了3秒,無論是否匹配成功都會繼續執行下一條
指令
expect "yes/no"
send "yes\n"
expect "*assword"
send "1\n"
expect eof

設定超時時間的目的僅僅只是為了讓指令碼不要一直卡在原地,要真正解決上述問題,需要改寫成下述形
式
#!/usr/bin/expect -f
spawn ssh [email protected] hostname
# 注意
# 1、{}一定要換行
# 2、下述語句就一個expect,代表匹配了一次,會匹配完一行就匹配下一行
expect {
"yes/no" {send "yes\r";exp_continue}
"*assword" {send "1\n"}
}
expect eof

3)練習

[root@aliyun ~]# cat 1.sh
#!/usr/bin/expect -f
spawn ssh [email protected]
set timeout -1 # 設定為-1代表永不超時,如果expect沒有捕捉到就一直停在原地
expect {
"yes/no" {send "yes\n"}
}
expect {
"password" {send "1\n"}
}
expect "*egon*"
send "ls\n"
expect "\$"
send "pwd\n"
expect "\$"
send "exit\n" # 注意一定要輸入exit結束訊號
expect eof # 最後關閉匹配
[root@aliyun ~]#

4)interact互動

interact :執行完成後保持互動狀態,把控制權交給控制檯,這個時候就可以手工操作了。如果沒有
這一句登入完成後會退出,而不是留在遠端終端上。

[root@egon ~]# cat test.sh
#!/usr/bin/expect -f
spawn ssh [email protected]
expect {
"yes/no" {send "yes\r";exp_continue}
"*assword" {send "1\n"}
}
interact
[root@egon ~]#
[root@egon ~]# ./test.sh
spawn ssh [email protected]
[email protected]'s password:
Last login: Wed Aug 26 21:28:04 2020 from egon
+--------------------------------------------+
| |
| 你當前登入的是支付業務後臺資料庫服務 |
| 請不要刪庫 |
| |
+--------------------------------------------+
[root@egon ~]# pwd
/root
[root@egon ~]# echo "hello"
hello
[root@egon ~]# exit
登出
Connection to 192.168.12.20 closed.
[root@egon ~]#

三、expect指令碼傳參

shell指令碼中的變數無法直接在expect中使用的,若expect需要使用變數一方面可以自己定義

#!/usr/bin/expect -f
set timeout -1
set user "root"
set ip "192.168.12.20"
set cmd "hostname"
set pass "1"
spawn ssh $user@$ip $cmd
expect {
"yes/no" {send "yes\r";exp_continue}
"*assword" {send "$pass\n"}
}
expect eof

另外一方面可以通過下述方式引入shell變數,注意此時直譯器換成#!/bin/bash

#!/bin/bash
user="root"
ip="192.168.12.20"
cmd="hostname"
pass="1"
expect << EOF
spawn ssh $user@$ip $cmd
expect {
"yes/no" {send "yes\r";exp_continue}
"*assword" {send "$pass\n"}
}
expect eof
EOF
此外,expect指令碼還可以從命令列獲取引數
在expect中, $argc 表示引數個數,而引數值存放在 $argv 中,比如取第一個引數就是 [lindex
$argv 0] ,以此類推。

[root@egon ~]# cat test.sh
#!/usr/bin/expect -f
if {$argc != 4} {
puts "Usage:./script.sh <ip> <username> <password> <cmd>"
exit 1
}
set ip [lindex $argv 0]
set user [lindex $argv 1]
set pass [lindex $argv 2]
set cmd [lindex $argv 3]
set timeout -1
spawn ssh $user@$ip $cmd
expect {
"yes/no" {send "yes\r";exp_continue}
"*assword" {send "$pass\n"}
}
expect eof
[root@egon ~]# ./test.sh
Usage:./script.sh <ip> <username> <password> <cmd>
[root@egon ~]# ./test.sh 192.168.12.20 root 1 hostname
spawn ssh [email protected] hostname
[email protected]'s password:
egon
[root@egon ~]#

四、grep介紹

1)grep簡介

grep命令主要用於過濾文字,grep家族如下
	1)grep: 在檔案中全域性查詢指定的正則表示式,並列印所有包含該表示式的行
	2)egrep:擴充套件的egrep,支援更多的正則表示式元字元
	3)fgrep:固定grep(fixed grep),有時也被稱作快速(fast grep),它按字面解釋所有的字元

2)grep命令格式

grep [選項] PATTERN 檔案1 檔案2 ...
[root@egon ~]# grep 'root' /etc/passwd
[root@egon ~]# fgrep 'bash' /etc/passwd
找到: grep返回的退出狀態為0
沒找到: grep返回的退出狀態為1
找不到指定檔案: grep返回的退出狀態為2

grep 命令的輸入可以來自標準輸入或管道,而不僅僅是檔案,例如:
ps aux |grep 'nginx'

五、grep選項

-n, --line-number 在過濾出的每一行前面加上它在檔案中的相對行號
-o, --only-matching 只顯示匹配的內容
-q, --quiet, --silent 靜默模式,沒有任何輸出,得用$?來判斷執行成功沒有,即有沒有過濾
到想要的內容
--color 顏色
-i, --ignore-case 忽略大小寫
-A, --after-context=NUM 如果匹配成功,則將匹配行及其後n行一起打印出來
-B, --before-context=NUM 如果匹配成功,則將匹配行及其前n行一起打印出來
-C, --context=NUM 如果匹配成功,則將匹配行及其前後n行一起打印出來
-c, --count 如果匹配成功,則將匹配到的行數打印出來
-v, --invert-match 反向查詢,只顯示不匹配的行
-w 匹配單詞
-E 等於egrep,擴充套件
-l, --files-with-matches 如果匹配成功,則只將檔名打印出來,失敗則不列印
通常-rl一起用,grep -rl 'root' /etc
-R, -r, --recursive 遞迴

六、grep示例

# 1、-n
[root@egon ~]# grep -n 'root' /etc/passwd
1:root:x:0:0:root:/root:/bin/bash
10:operator:x:11:0:operator:/root:/sbin/nologin
[root@egon ~]#
# 2、-o
[root@egon ~]# grep -o 'root' /etc/passwd
root
root
root
root
[root@egon ~]#
# 3、-q
[root@egon ~]# grep -q 'root' /etc/passwd
[root@egon ~]# echo $?
0
# 4、--color
[root@egon ~]# alias grep
alias grep='grep --color=auto'
[root@egon ~]#
# 5、-i
[root@egon ~]# echo "EGON" |grep -i egon
EGON
[root@egon ~]#
# 6、-A\-B\-C
[root@egon ~]# grep -A 2 'root' /etc/passwd
[root@egon ~]# grep -B 2 'root' /etc/passwd
[root@egon ~]# grep -C 2 'root' /etc/passwd
# 7、-c
[root@egon ~]# grep -c 'root' /etc/passwd
2
[root@egon ~]#
# 8、-v
[root@egon ~]# ps aux | grep nginx |grep -v grep
[root@egon ~]#
[root@egon ~]# ps aux | grep [n]ginx
[root@egon ~]#
# 9、-w
[root@egon ~]# netstat -an |grep -w 80
tcp6 0 0 :::80 :::* LISTEN 
[root@egon ~]# netstat -an |grep '\<80\>'
tcp6 0 0 :::80 :::* LISTEN 
[root@egon ~]# netstat -an |grep '\b80\b'
tcp6 0 0 :::80 :::* LISTEN 

# 10、-rl
[root@egon ~]# grep -rl 'root' /etc # 將/etc目錄下所有包含'root'內容的檔案都列出來

七、正則表示式

1)正則表示式介紹

1.正則表示式,又稱規則表示式。(英語:Regular Expression,在程式碼中常簡寫為regex、regexp或RE),是電腦科學的一個概念。正則表示式由元字元組成,通常被用來檢索、替換那些符合某個模式(規則)的文字(許多程式設計語言都支援利用正則表示式進行字串操作)。

2.元字元:是一類可以表達出超越其字面本身含義的特殊字元
shell元字元(也稱為萬用字元): 由shell直譯器來解析,如rm -rf *.pdf,元字元*Shell將其解析為任意多個字元
正則表示式元字元 : 由各種執行模式匹配操作的程式來解析,比如vi、grep、sed、awk

3.例如:vim示例:
:1,$ s/tom/EGON/g # 如anatomy、tomatoes及tomorrow中的“tom”被替換了,而Tom確沒
被替換
:1,$ s/\<[Tt]om\>/EGON/g

2)正則表示式元字元

元字元 功能 示例
^ 行首 ^love
$ 行尾 love$
. 除了換行符以外的任意單個字元 l..e
* 前導字元的零個或多個 ab*love
.* 所有字元 a.*love
[] 字元組內的任一字元 [lL]ove
[^] 對字元組內的每個字元取反(不匹配字元組內的每個字元) [^a-z0-9]ove
^[^] 非字元組內的字元開頭的行
[a-z] 小寫字母
[A-Z] 大寫字母
[a-Z] 小寫和大寫字母
[0-9] 數字
\ 用來轉義元字元 love\.
\< 詞首定位符 單詞一般以空格或特殊字元做分隔、連續的字元組成 \<love
\> 詞尾定位符 love\>
\(..\) 匹配稍後將要使用的字元的標籤 \(love\)able\1er
:1,$ s/\						(192.168.11\).66/\1.50/g
x\{m\} 字元x重複出現m次 e\{3\}
x\{m,\} 字元x重複出現m次以上 e\{3,\}
x\{m,n\} 字元x重複出現m到n次 e\{3,6\}

3)正則表示式示例

# 1、^ 行首
[root@egon ~]# grep '^root' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@egon ~]#
# 2、$ 行尾
[root@egon ~]# grep 'bash$' /etc/passwd
root:x:0:0:root:/root:/bin/bash
user1:x:1002:1003::/home/user1:/bin/bash
egon1:x:198:1005::/home/egon1:/bin/bash
gg:x:1004:1006::/home/gg:/bin/bash
egon:x:1005:1007::/home/egon:/bin/bash
tom:x:1006:1008::/home/tom:/bin/bash
[root@egon ~]#
# 3、. 除了換行符以外的任意單個字元
[root@egon ~]# grep 'r..t' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
[root@egon ~]#
# 4、* 前導字元的零個或多個
[root@egon ~]# cat a.txt
a
ab
abb
abbb
bbbbb
[root@egon ~]# grep 'ab*' a.txt
a
ab
abb
abbb
[root@egon ~]#
# 5、.* 所有字元=貪婪
[root@egon ~]# cat a.txt
a123+-*/c11113333c
a1c
a77Ac
a23333c
ac
111
222
333
[root@egon ~]# grep 'a.*c' a.txt
a123+-*/c11113333c
a1c
a77Ac
a23333c
ac
[root@egon ~]#
# 5.1 .*?=》非貪婪,預設情況下,grep不支援非貪婪修飾符,但您可以使用grep -P來使用Perl語法來
支援.*?
[root@egon ~]# cat a.txt
<a href="http://www.baidu.com">"我他媽的是百度"</a>
<a href="http://www.sina.com.cn">"我特麼的是新浪"</a>
[root@egon ~]#
[root@egon ~]# grep -o 'href=".*"' a.txt # 貪婪
href="http://www.baidu.com">"我他媽的是百度"
href="http://www.sina.com.cn">"我特麼的是新浪"
[root@egon ~]#
[root@egon ~]# grep -oP 'href=".*?"' a.txt # 非貪婪
href="http://www.baidu.com"
href="http://www.sina.com.cn"
[root@egon ~]#
# 6、[] 字元組內的任一字元
# 7、[^] 對字元組內的每個字元取反(不匹配字元組內的每個字元)
[root@egon ~]# cat a.txt
a1c
a2c
a33c
aAc
aZc
[root@egon ~]# grep 'a[0-9]c' a.txt
a1c
a2c
[root@egon ~]# grep 'a[^0-9]c' a.txt
aAc
aZc
[root@egon ~]#
[root@egon ~]# grep 'a[0-9][0-9]c' a.txt
a33c
[root@egon ~]#
# 8、^[^] 非字元組內的字元開頭的行
[root@egon ~]# cat a.txt
a1c
a2c
a33c
aAc
aZc
[root@egon ~]# grep '^[^0-9]..$' a.txt
a1c
a2c
aAc
aZc
[root@egon ~]#
# 9、[a-z] 小寫字母
# 10、[A-Z] 大寫字母
# 11、[a-Z] 小寫和大寫字母
# 12、[0-9] 數字
# 13、\< 單詞頭 單詞一般以空格或特殊字元做分隔,連續的字串被當做單詞
# 14、\> 單詞尾
[root@egon ~]# netstat -an |grep -w 80
tcp6 0 0 :::80 :::* LISTEN 
[root@egon ~]# netstat -an |grep '\<80\>'
tcp6 0 0 :::80 :::* LISTEN 
[root@egon ~]# netstat -an |grep '\b80\b'
tcp6 0 0 :::80 :::* LISTEN 
Ps: grep匹配換行符和製表符
[root@egon ~]# echo -e "a\nb" |grep $'a\nb'
a
b
[root@egon ~]#
[root@egon ~]# echo -e "a\tb" |grep $'a\tb'
a b
[root@egon ~]#

4)擴充套件正則元字元

# 擴充套件正則元字元
+ 匹配一個或多個前導字元 [a-z]+ove
? 匹配零個或一個前導字元 lo?ve
a|b 匹配a或b love|hate
() 組字元 love(able|rs) (egon)+
(..)(..)\1\2 標籤匹配字元 (love)able\1er
x{n} x出現n次 e{3}
x{n,} x出現n次至無窮次 e{3,}
x{n,m} x出現n次至m次 e{3,6}

# 若想使用擴充套件正則
grep加-E 或 egrep 或轉義\
sed 加 -r 引數 或轉義
AWK 直接支援大多數擴充套件正則,更多支援需要加選項--posix選項

5)擴充套件元字元示例

# ======================grep擴充套件正則示例======================
[root@egon ~]# cat a.txt
a
ab
abb
abbb
abbbb
abbbbb
bbbbbbb
[root@egon ~]# grep 'ab{2,4}' a.txt # 預設不支援擴充套件正則,所以沒效果
[root@egon ~]# egrep 'ab{2,4}' a.txt
abb
abbb
abbbb
abbbbb
[root@egon ~]#
# ======================sed擴充套件正則示例======================
[root@egon ~]# sed -n '/roo?/p' /etc/passwd # 預設不支援擴充套件正則?
[root@egon ~]# sed -n '/roo\?/p' /etc/passwd # 可以用\轉義擴充套件正則符號?
有結果,結果略...
[root@egon ~]# sed -rn '/roo?/p' /etc/passwd # 也可以加-r選項
有結果,結果略...
[root@egon ~]#
# ======================awk擴充套件正則示例======================
[root@egon ~]# cat a.txt
a
ab
abb
abbb
abbbb
abbbbb
bbbbbbb
[root@egon ~]# awk '/ab{1,3}/{print}' a.txt
ab
abb
abbb
abbbb
abbbbb
[root@egon ~]# awk --posix '/ab{1,3}/{print}' a.txt
ab
abb
abbb
abbbb
abbbbb
[root@egon ~]#

6)總結

grep: 使用基本元字符集 ^, $, ., *, [], [^], \< \>,\(\),\{\}
egrep(或grep -E): 使用擴充套件元字符集 ?, +, { }, |, ( )
# 注:grep也可以使用擴充套件集中的元字元,僅需要對這些元字元前置一個反斜線
\w 所有字母與數字,稱為字元[a-zA-Z0-9] 'l[a-zA-Z0-9]*ve' 'l\w*ve'
\W 所有字母與數字之外的字元,稱為非字元 'love[^a-zA-Z0-9]+' 'love\W+'
\b 詞邊界 '\blove\b' '\<love\>'

7)POSIX定義的字元分類

# 表示式 功能 示例
[:alnum:] 字母與數字字元 [[:alnum:]]+ 
[:alpha:] 字母字元(包括大小寫字母) [[:alpha:]]{4}
[:blank:] 空格與製表符 [[:blank:]]*
[:digit:] 數字字母 [[:digit:]]?
[:lower:] 小寫字母 [[:lower:]]{5,}
[:upper:] 大寫字母 [[:upper:]]+
[:punct:] 標點符號 [[:punct:]]
[:space:] 包括換行符,回車等在內的所有空白[[:space:]]+
# 詳解
[:alnum:] Alphanumeric characters.
匹配範圍為 [a-zA-Z0-9]
[:alpha:] Alphabetic characters.
匹配範圍為 [a-zA-Z]
[:blank:] Space or tab characters.
匹配範圍為 空格和TAB鍵
[:cntrl:] Control characters.
匹配控制鍵 例如 ^M 要按 ctrl+v 再按回車 才能輸出
[:digit:] Numeric characters.
匹配所有數字 [0-9]
[:graph:] Characters that are both printable and visible. (A space is print-
able, but not visible, while an a is both.)
匹配所有可見字元 但不包含空格和TAB 就是你在文字文件中按鍵盤上能用眼睛觀察到的所有符號
[:lower:] Lower-case alphabetic characters.
小寫 [a-z]
[:print:] Printable characters (characters that are not control characters.)
匹配所有可見字元 包括空格和TAB
能列印到紙上的所有符號
[:punct:] Punctuation characters (characters that are not letter, digits, con-
trol characters, or space characters).
特殊輸入符號 +-=)(*&^%$#@!~`|\"'{}[]:;?/>.<,
注意它不包含空格和TAB
這個集合不等於^[a-zA-Z0-9]
[:space:] Space characters (such as space, tab, and formfeed, to name a few).
[:upper:] Upper-case alphabetic characters.
大寫 [A-Z]
[:xdigit:] Characters that are hexadecimal digits.
16進位制數 [0-f]
# 使用方法:
[root@egon ~]# grep --color '[[:alnum:]]' /etc/passwd

八、練習

目標檔案/etc/passwd,使用grep命令或egrep
1.顯示出所有含有root的行:
2.輸出任何包含bash的所有行,還要輸出緊接著這行的上下各兩行的內容:
3. 顯示出有多少行含有nologin。
4.顯示出那些行含有root,並將行號一塊輸出。
5.顯示出檔案中
6.新建使用者
  abominable
abominate
anomie
atomize
編寫正則表示式,將他們匹配出來
egrep 'a.omi(nabl|nat|z|)e' /etc/passwd
7.建四個使用者
Alex213sb
Wpq2222b
yH438PIG
egon666
egon
過濾出使用者名稱組成是字母+數字+字母的行
[root@MiWiFi-R3-srv ~]# egrep '^[a-Z]+[0-9]+[a-Z]+' /etc/passwd
8.顯示出/etc目錄下所有包含root的檔名
9. 過濾掉/etc/ssh/sshd_config內所有註釋和所有空行
grep -v '^#' /etc/ssh/sshd_config |grep -v '^ *$'