概述:
shell是我們和linux接觸的通道,靈活使用使用shell就能更好的掌控linux,想學好shell首先是要理解熟記很多的命令,這就相當於英語積累的單詞一樣,單詞記得多了,英語自然就好了,但是想要真正變成英語達人還要了解些語法,shell也是一樣,命令是單詞,也有他的語法,我們這次主要討論語法部分。
expect是一個免費的編程工具語言,用來實現自動和交互式任務進行通信,而無需人的干預。expect是不斷髮展的,隨着時間的流逝,其功能越來越強大,已經成爲系統管理員的的一個強大助手。簡單的說,如果沒有expect的話很多需要交互的場景我們就需要一直守在計算機前面進行輸入,如果輸入是主要步驟還好,我們輸入完就結束任務了,那萬一,我們有一個任務是在凌晨3點以root身份執行一個腳本呢,我們需要的輸入只是一個密碼,卻要一直在電腦前等到3點,想想都很虧。
1 bash的一些內建參數
1.1 "!"的妙用
!*:上一個命令的所有參數
[root@foundation3 mnt]# ls -a -l
total 41616
drwxrwxrwx. 3 root root 84 Dec 13 17:16 .
。。。省略
[root@foundation3 mnt]# echo !*
echo -a -l
-a -l
!^:上一條命令的第一個參數
[root@foundation3 mnt]# ls -a -l
total 41616
drwxrwxrwx. 3 root root 84 Dec 13 17:16 .
。。。省略
[root@foundation3 mnt]# echo !^
echo -a
-a
!$:上一條命令的最後一個參數[root@foundation3 mnt]# ls -a -l
total 41616
drwxrwxrwx. 3 root root 84 Dec 13 17:16 .
。。。省略
[root@foundation3 mnt]# echo !$
echo -l
-l
!#:意義不明。。。。
[root@foundation3 mnt]# echo !#
echo echo
echo
!<comm>:<num>:顯示上一個comm命令的第num個參數
[root@foundation3 mnt]# cp scan /media/
[root@foundation3 mnt]# echo !cp:2
echo /media/
/media/
[root@foundation3 mnt]# echo !cp:1
echo scan
scan
!(file):除去file代表的文件
[root@foundation3 tmp]# ls
file10.pdf file1.txt file3.pdf file4.txt file6.pdf file7.txt file9.pdf
file10.txt file2.pdf file3.txt file5.pdf file6.txt file8.pdf file9.txt
file1.pdf file2.txt file4.pdf file5.txt file7.pdf file8.txt file.pdf
[root@foundation3 tmp]# rm -rf !(*.pdf)
[root@foundation3 tmp]# ls
file10.pdf file2.pdf file4.pdf file6.pdf file8.pdf file.pdf
file1.pdf file3.pdf file5.pdf file7.pdf file9.pdf
上面的演示就是把.pdf結尾以外的文件都刪除。
1.2 shell的位置參數
位置參數的個數:$#(通常用來判斷是否加入了參數)
某個位置參數:$0,$1,$2 ...
所有位置參數:$@,$*
退出的狀態:$? (執行shell成功後退出狀態是0,如果出現錯誤的話,退出狀態就是非0,退出狀態由exit num的num決定)
2.shell的test條件判斷
test判斷條件可以用[]代替,用法是完全一樣的如下:
[root@foundation3 tmp]# a=1;b=2
[root@foundation3 tmp]# [ $a -eq $b ];echo $?
1
[root@foundation3 tmp]# test $a -eq $b;echo $?
1
上面的兩種用法是完全一樣的2.1 字符比較:
使用=和!=
[root@foundation3 tmp]# str1="mo";str2="momo"
[root@foundation3 tmp]# [ $str1 != $str2 ];echo $?
0
2.2 數值比較:
-eq,-ne,-lt,-le,-gt,-ge
分別是相等(equal),不等(not equal),小於(less than),小於等於(less equal),大於(greater than),大於等於(greater equal)
和英語結合起來記憶就非常簡單了。
2.3文件狀態運算符
-b:塊文件
-c:字符文件
-e:文件存在
-f:普通文件
-d:目錄
-r:文件可讀
-w:文件可寫
-x:文件可執行
-s:文件大小非0爲真
-L:文件爲軟鏈接的時候爲真
2.3邏輯運算符
邏輯運算符有-o,-a,!,&&,||
這裏也是經常出錯的地方因爲,有相當於兩套邏輯運算,前面講了那麼多現在我們來練習一下。
練習1:
判斷一個文件既是普通文件又是軟鏈接文件
寫法1
[root@foundation3 tmp]# [ -f b ] && [ -L b ];echo $?
0
[root@foundation3 tmp]# ll b
lrwxrwxrwx 1 root root 1 Dec 14 15:36 b -> a
寫法2
[root@foundation3 tmp]# [ -f b -a -L b ];echo $?
0
練習2:
判斷一個數的範圍(x>=3,x<=5)
寫法1:
[root@foundation3 tmp]# [ $x -ge 3 ] && [ $x -le 5 ];echo $?
1
寫法2:
[root@foundation3 tmp]# [ $x -ge 3 -a $x -le 5 ];echo $?
1
[root@foundation3 tmp]# [ 4 -ge 3 -a 4 -le 5 ];echo $?
0
[root@foundation3 tmp]# echo $x
10
這之前先理解下$與括號的用法
$():命令替換的意思與雙反引號意思一樣``。
${}:引用變量比如變量賦值的時候是a=1,使用的時候就要使用$a或者${a}。
$[]:表示數值運算
(()):表示數字運算,是bash的內建功能效率很高。
let num=1+2:表示數值運算
expr 1+2:命令表示數值運算
綜上,總共有四種之多的方法表示數值運算,是不是有點記混了,不急咱們一個個看區別,多敲就會記住了。
$[]:
[root@foundation3 mnt]# echo $[1+2]
3
[root@foundation3 mnt]# echo $[$[1+2]+$[3+4]]
10
[root@foundation3 mnt]# echo [$[1+2]+$[3+4]]
[3+7]
[root@foundation3 mnt]# echo $[1+2]+$[3+4]
3+7
[root@foundation3 mnt]# echo $[$j+=1]
-bash: 3+=1: attempted assignment to non-variable (error token is "+=1")
[root@foundation3 mnt]# echo $[$j+1]
4
可見在[]中一定還要用$引用變量(()):
[root@foundation3 mnt]# echo $((1+2))
3
[root@foundation3 mnt]# echo $j
6
[root@foundation3 mnt]# ((j++))
[root@foundation3 mnt]# echo $j
7
(())就不用$引用變量
let:
[root@foundation3 mnt]# let a=2
[root@foundation3 mnt]# echo $a
2
[root@foundation3 mnt]# let a=a+2
[root@foundation3 mnt]# echo $a
4
[root@foundation3 mnt]# let a++
[root@foundation3 mnt]# echo $a
5
let也不需要$引用expr:
expr的功能很強大,我們只是簡單用一下他的運算功能
由於expr是個命令所以輸入的數值和運算符都要有空格,也就是通過命令行參數的方式傳到expr裏面
[root@foundation3 mnt]# expr 1 + 2
3
[root@foundation3 mnt]# expr $a + 1
1
4.if語句
if語句用來檢查後面命令的退出值的,如果爲true則運行then後的語句,一直到fi或者else。而且if經常和test搭配使用。
用一個題目來看下if的用法
如果http服務開啓的話就關閉,如果httpd關閉則開啓
if systemctl is-active httpd;then
systemctl stop httpd
else
systemctl start httpd
fi
5.case語句
這個語句相當於簡化了多分支if的寫法,有時候需要多次判斷一個值的情況,使用if寫起來會有點繁瑣,比如下面的情況。
#!/bin/bash
arg=$1
case "$arg" in
apple )
echo "apple"
;;
orange )
echo "orange"
;;
banana )
echo "banana"
;;
pig )
echo "pig"
;;
* )
echo "else"
;;
esac
6.expect腳本
expect腳本功能十分強大,但我們不用掌握太多,只用掌握如下的幾條語句,暫時就夠用了。
spawn:開啓一個進程,接下來會對這個進程進行監控
set a ?:設置一個變量的值
[lindex $argv 0]:接收命令行傳進來的參數
expect,send:這兩條是核心命令了,其實就相當於對一個對話的處理
a:hello
b:nihao
a:goodbye
b:byebye
用腳本表示就是
expect "hello" {send "nihao"}
expect "goodbye" {send "byebye"}
。
最後有一個關於expect的小腳本,需求是,掃描你當前所在局域網的主機,如果可以ping通的,就要登陸進去,並且創建好一個用戶,如果這個用戶存在了,不要更改人家的密碼。
代碼如下:
scan (expect腳本):
#!/usr/bin/expect
set timeout 1
set RET 0
set ipaddr [lindex $argv 0]
set password [lindex $argv 1]
set user "mo"
set u_pass "123"
spawn ssh root@$ipaddr
expect {
"yes/no"
{send "yes\r";exp_continue}
"password"
{send "$password\r"}
}
expect "*Permission denied" {set RET 1}
expect "]#" {send "id $user\r"}
expect {
"uid"
{send "exit\r"}
"no such user"
{send "useradd $user\r"}
}
#expect "]#" {send "passwd $user\r";exp_continue}
#expect "New password:" {send "$u_pass\r";exp_continue}
#expect "Retype new password:" {send "$u_pass\r";exp_continue}
expect "]#" {
send "echo $u_pass | passwd --stdin $user\r"
}
expect "]#" { send "exit\r"}
scan_class.sh(shell腳本)
#!/bin/bash
db_name="scan_data"
ip_pre="172.25.254."
check_data()
{
mysql -uscan -predhat -e "select password from scan_data.user where ipaddr=\"$1\";" -NE | grep -v "row"
}
if [ -z `mysql -uscan -predhat -e "show databases;" -NE | grep $db_name` ];then
echo "database not exist"
fi
if [ ! -e /mnt/scan ];then
echo "scan shell is not found"
fi
for i in {10..11};do
ip=${ip_pre}$i
if ping -c1 -w1 >/dev/null $ip;then
pass=`check_data $ip`
/mnt/scan $ip $pass | grep -vE "^spawn|^root|^Last|[[]root|^logout|^Connect|^The|^ECDSA|^Warning|^Are" | sed "s/^uid.*/user is already exist in $ip/g" | sed "s/.*no such user.*$/user add success/g" | sed "s/^Permission denied.*/$ip password is wrong/g"
fi
done
簡單測試一下
[root@foundation3 mnt]# ./scan_class.sh
172.25.254.10 password is wrong
user is already exist in 172.25.254.11
我對我的兩臺虛擬機進行了測試第一臺密碼錯誤了,第二臺已經擁有了那個用戶。
[root@foundation3 mnt]# ./scan_class.sh
user add success
user add success
添加成功的信息,額。。。好像沒有ip很彆扭,加上吧。
[root@foundation3 mnt]# ./scan_class.sh
172.25.254.10 user add success
172.25.254.11 user add success
這個腳本的主機賬戶信息是存儲在數據庫中的,所以每次只要通過sql語句查找ip對應的密碼即可連接。但是由於在shell中爲了防止每次查找數據都輸入密碼,所以選擇了將數據可密碼暴露的形式,爲了安全起見,特意創建一個新用戶來作爲這個腳本的查詢用戶。
數據庫如下:
+------+---------------+----------+
| uid | ipaddr | password |
+------+---------------+----------+
| 0 | 172.25.254.10 | redhat |
| 1 | 172.25.254.11 | redhat |
+------+---------------+----------+