運維筆記23 (shell腳本,expect的簡易用法)

概述:

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


3.shell的數值運算
這之前先理解下$與括號的用法

$():命令替換的意思與雙反引號意思一樣``。

${}:引用變量比如變量賦值的時候是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   |
+------+---------------+----------+










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