1. 程式人生 > 其它 >shell隨機數和巢狀迴圈

shell隨機數和巢狀迴圈

一、隨機數

bash預設有一個$RANDOM的變數        預設是0~32767。使用set |grep RANDOM    檢視上一次產生的隨機數
echo $RANDOM

產生0~1之間的隨機數
echo $[$RANDOM%2]

產生0~2之間的隨機數
echo $[$RANDOM%3]

產生0~3之間的隨機數
echo $[$RANDOM%4]
。。。。
產生0~9內的隨機數
echo $[$RANDOM%10]

產生0~100內的隨機數
echo $[$RANDOM%101]

產生10~99的隨機數
echo $[$RANDOM%90+10] 產生50
-100之內的隨機數 echo $[$RANDOM
%51+50] 產生三位數的隨機數 echo $[$RANDOM%900+100]

實戰案例1

  1. 寫一個指令碼,產生一個phonenum.txt檔案,隨機產生以139開頭的手機號1000個,每個一行。

分析:
1. 產生1000個電話號碼,指令碼需要迴圈1000次
2. 139+8位,後8位隨機產生,可以讓每一位數字都隨機產生,$[RANDOM%10] 0-9
3. 將隨機產生的數字分別儲存到變數裡,然後加上139儲存到檔案裡

#!/bin/bash
# random phonenum
# 迴圈1000次產生電話號碼並儲存到檔案
for i in {1..1000}
do
    n1=$[RANDOM%10]
    n2=$[RANDOM%10
] n3=$[RANDOM%10] n4=$[RANDOM%10] n5=$[RANDOM%10] n6=$[RANDOM%10] n7=$[RANDOM%10] n8=$[RANDOM%10] echo "139$n1$n2$n3$n4$n5$n6$n7$n8" >> phonenum.txt done #!/bin/bash # random phonenum # 迴圈1000次產生電話號碼 for ((i=1;i<=1000;i++)) do n1=$[$RANDOM%10] n2=$[$RANDOM%10] n3=$[$RANDOM%10
] n4=$[$RANDOM%10] n5=$[$RANDOM%10] n6=$[$RANDOM%10] n7=$[$RANDOM%10] n8=$[$RANDOM%10] echo "139$n1$n2$n3$n4$n5$n6$n7$n8" >> phonenum.txt done #!/bin/bash i=1 while [ $i -le 1000 ] do n1=$[$RANDOM%10] n2=$[$RANDOM%10] n3=$[$RANDOM%10] n4=$[$RANDOM%10] n5=$[$RANDOM%10] n6=$[$RANDOM%10] n7=$[$RANDOM%10] n8=$[$RANDOM%10] echo "139$n1$n2$n3$n4$n5$n6$n7$n8" >> phonenum.txt let i++ done continue:繼續,跳過本次迴圈,執行下一次迴圈 break:打斷,執行迴圈體外的程式碼do..done外 exit:退出程式 #!/bin/bash for i in {1..1000} do n1=$[$RANDOM%10] n2=$[$RANDOM%10] n3=$[$RANDOM%10] n4=$[$RANDOM%10] n5=$[$RANDOM%10] n6=$[$RANDOM%10] n7=$[$RANDOM%10] n8=$[$RANDOM%10] echo "139$n1$n2$n3$n4$n5$n6$n7$n8" >> phonenum.txt done #!/bin/bash #create phone num file for ((i=1;i<=1000;i++)) do n1=$[$RANDOM%10] n2=$[$RANDOM%10] n3=$[$RANDOM%10] n4=$[$RANDOM%10] n5=$[$RANDOM%10] n6=$[$RANDOM%10] n7=$[$RANDOM%10] n8=$[$RANDOM%10] echo "139$n1$n2$n3$n4$n5$n6$n7$n8" |tee -a phonenum.txt done #!/bin/bash count=0 while true do n1=$[$RANDOM%10] n2=$[$RANDOM%10] n3=$[$RANDOM%10] n4=$[$RANDOM%10] n5=$[$RANDOM%10] n6=$[$RANDOM%10] n7=$[$RANDOM%10] n8=$[$RANDOM%10] echo "139$n1$n2$n3$n4$n5$n6$n7$n8" |tee -a phonenum.txt && let count++ if [ $count -eq 1000 ];then break fi done
  1. 在上面的1000個手機號裡抽獎5個幸運觀眾,顯示出這5個幸運觀眾。但只顯示頭3個數和尾號的4個數,中間的都用*代替

思路:

  • 確定幸運觀眾所在的行 隨機生成 RANDOM $[RANDOM%1000+1]

  • 將電話號碼提取出來 head 和 tail

  • 顯示前3個和後4個數到螢幕 最後將電話號碼輸出到螢幕 echo ${電話號碼部分}

#!/bin/bash
#定義變數
phone=/shell04/phonenum.txt
for ((i=1;i<=5;i++))
do
    #定位幸運觀眾所在行號
    line=`wc -l $phone |cut -d' ' -f1`   #wc -l 會顯示總行數和檔名,這裡只要總行數
    luck_line=$[RANDOM%$line+1]
    #取出幸運觀眾所在行的電話號碼
    luck_num=`head -$luck_line $phone|tail -1`       #head -3 phonenum.txt |grep tail 1 顯示前三行,再顯示這三行的最後一行
    #顯示到螢幕
    echo "139****${luck_num:7:4}"
    echo $luck_num >> luck.txt
    #刪除已經被抽取的幸運觀眾號碼
    sed -i "/$luck_num/d" $phone   #-i 直接對內容進行修改,不加-i時預設只是預覽,不會對檔案做實際修改
done



#!/bin/bash
file=/shell04/phonenum.txt
for i in {1..5}
do
    file_num=`wc -l $file |cut -d' ' -f1`
    line=`echo $[$RANDOM%$file_num+1]`
    luck=`head -n $line  $file |tail -1`
    echo "139****${luck:7:4}" && echo $luck >> /shell04/luck_num.txt
done


#!/bin/bash
for ((i=1;i<=5;i++))
do
file=phonenum.txt                  #指令碼得和檔案再同一個路徑下
line=`cat phonenum.txt |wc -l`    
luckline=$[$RANDOM%$line+1]
phone=`cat $file|head -$luckline|tail -1`
echo "幸運觀眾為:139****${phone:7:4}"
done


或者
#!/bin/bash
# choujiang
phone=phonenum.txt
for ((i=1;i<=5;i++))
do
    num=`wc -l phonenum.txt |cut -d' ' -f1`
    line=`echo $[$RANDOM%$num+1]`
    luck=`head -$line $phone |tail -1`
    sed -i "/$luck/d" $phone
    echo "幸運觀眾是:139****${luck:7:4}"
done
  1. 批量建立5個使用者,每個使用者的密碼為一個隨機數

思路:

  • 迴圈5次建立使用者

  • 產生一個密碼檔案來儲存使用者的隨機密碼

  • 從密碼檔案中取出隨機密碼賦值給使用者

#!/bin/bash
#crate user and set passwd
#產生一個儲存使用者名稱和密碼的檔案
echo user0{1..5}:itcast$[$RANDOM%9000+1000]#@~|tr ' ' '\n'>> user_pass.file
#迴圈建立5個使用者
for ((i=1;i<=5;i++))
do
    user=`head -$i user_pass.file|tail -1|cut -d: -f1`
    pass=`head -$i user_pass.file|tail -1|cut -d: -f2`
    useradd $user
    echo $pass|passwd --stdin $user
done

或者
for i in `cat user_pass.file`
do
    user=`echo $i|cut -d: -f1`
    pass=`echo $i|cut -d: -f2`
    useradd $user
    echo $pass|passwd --stdin $user
done

#!/bin/bash
#crate user and set passwd
#產生一個儲存使用者名稱和密碼的檔案
echo user0{1..5}:itcast$[$RANDOM%9000+1000]#@~|tr ' ' '\n'|tr ':' ' ' >> user_pass.file
#迴圈建立5個使用者
while read user pass
do
useradd $user
echo $pass|passwd --stdin $user
done < user_pass.file


pwgen工具產生隨機密碼:
[root@server shell04]# pwgen -cn1 12
Meep5ob1aesa
[root@server shell04]# echo user0{1..3}:$(pwgen -cn1 12)
user01:Bahqu9haipho user02:Feiphoh7moo4 user03:eilahj5eth2R
[root@server shell04]# echo user0{1..3}:$(pwgen -cn1 12)|tr ' ' '\n'
user01:eiwaShuZo5hi
user02:eiDeih7aim9k
user03:aeBahwien8co

while 用ip.txt檔案賦予變數:

10.1.1.11 root 123
10.1.1.22 root 111
10.1.1.33 root 123456
10.1.1.44 root 54321
 

寫法1:

cat ip.txt | while read ip user pass
do
    echo "$ip--$user--$pass"
done
 

寫法2:

while read ip user pass
do
    echo "$ip--$user--$pass"
done < ip.txt
寫法3:
exec < ip.txt
while read ip user pass
do
  echo
"$ip--$user--$pass"
done
使用IFS作為分隔符讀檔案 說明:預設情況下IFS是空格,如果需要使用其它的需要重新賦值 IFS=: 例如: # cat test chen:222:gogo jie:333:hehe # cat test.sh #!/bin/bash IFS=: cat test | while read a1 a2 a3 do echo "$a1--$a2--$a3"

下載pwgen
需要配置epel源 #
yum install epel-release -y # yum install pwgen -y pwgen基本語法 # pwgen 選項引數 長度 生成個數 選項說明: # pwgen --help:幫助命令 -c:密碼中至少包含一個大寫字母 -A:密碼中不包含大寫字母 -n:密碼中至少包含一個數字 -0:密碼中不包含數字 -y:密碼中至少包含一個特殊符號 -s:生成完全隨機密碼 -B:密碼中不包含歧義字元(例如1,l,O,0-H or –sha1=path/to/file[#seed]:使用SHA1 hash給定的檔案作為一個隨機種子 -C:在列中列印生成的密碼 -1:不要在列中列印生成的密碼,即一行一個密碼(是一不是字母l) -v:不要使用任何母音,以避免偶然的髒話 案例:生成長度為10,包含大寫、數字、不包含模糊字元完全隨機的3個密碼 # pwgen -cnBn1 10 3

 

二、巢狀迴圈

一個==迴圈體==內又包含另一個完整的迴圈結構,稱為迴圈的巢狀。在外部迴圈的每次執行過程中都會觸發內部迴圈,直至內部完成一次迴圈,才接著執行下一次的外部迴圈。for迴圈、while迴圈和until迴圈可以相互巢狀。

demo1:列印如下圖案

1
12
123
1234
12345


X軸:
for ((i=1;i<=5;i++));do echo -n $i;done
Y軸:
負責列印換行

#!/bin/bash
for ((y=1;y<=5;y++))
do
    for ((x=1;x<=$y;x++))
    do
        echo -n $x  #-n取消換行
    done
echo
done

#!/bin/bash
for ((y=1;y<=5;y++))
do
    x=1
    while [ $x -le $y ]
        do
        echo -n $x
        let x++
        done
echo
done

demo2:列印如下圖案

5
54
543
5432
54321

Y軸:列印換行
X軸:列印數字 5-1

#!/bin/bash
y=5
while (( $y >= 1 ))
do
    for ((x=5;x>=$y;x--))
    do
        echo -n $x
    done
echo
let y--
done


#!/bin/bash
for (( y=5;y>=1;y--))
do
    for (( x=5;x>=$y;x--))
    do
    echo -n $x
    done
echo
done

#!/bin/bash
y=5
while [ $y -ge 1 ]
do
    for ((x=5;x>=$y;x--))
    do
    echo -n $x
    done
echo
let y--
done


#!/bin/bash
y=1
until (( $y >5 ))
do
    x=1
    while (( $x <= $y ))
    do
    echo -n $[6-$x]
    let x++
    done    
echo
let y++
done


課後列印:
54321
5432
543
54
5

#!/bin/bash
#列印
for ((y=1;y<=5;y++))
  do
  x=5
  while ((x>=y))
    do
      echo -n $x
      let x--
    done
  echo
done

 

課堂練習:列印九九乘法表(三種方法)

1*1=1

1*2=2   2*2=4

1*3=3   2*3=6   3*3=9

1*4=4   2*4=8   3*4=12  4*4=16

1*5=5   2*5=10  3*5=15  4*5=20  5*5=25

1*6=6   2*6=12  3*6=18  4*6=24  5*6=30  6*6=36

1*7=7   2*7=14  3*7=21  4*7=28  5*7=35  6*7=42  7*7=49

1*8=8   2*8=16  3*8=24  4*8=32  5*8=40  6*8=48  7*8=56  8*8=64

1*9=9   2*9=18  3*9=27  4*9=36  5*9=45  6*9=54  7*9=63  8*9=72  9*9=81


Y軸:迴圈9次,列印9行空行
X軸:迴圈次數和Y軸相關;列印的是X和Y軸乘積 $[] $(())

#!/bin/bash
for ((y=1;y<=9;y++))
do
    for ((x=1;x<=$y;x++))
    do
        echo -ne "$x*$y=$[$x*$y]\t"
    done
echo
echo
done


#!/bin/bash
y=1
while [ $y -le 9 ]
do
        x=1
        while [ $x -le $y ]
        do
                echo -ne "$x*$y=$[$x*$y]\t"
                let x++
        done
echo
echo
let y++
done

或者
#!/bin/bash
for i in `seq 9`
do
    for j in `seq $i`
    do
        echo -ne  "$j*$i=$[$i*$j]\t"
    done
echo
echo
done
或者
#!/bin/bash
y=1
until [ $y -gt 9 ]
do
        x=1
        until [ $x -gt $y ]
        do
                echo -ne "$x*$y=$[ $x*$y ]\t"
                let x++
        done
echo
echo
let y++
done

三、階段性總結

1. 變數定義

普通變數定義:
變數名=值        shell變數預設可以賦予任何型別
$變數名        ${變數名}        ${變數名:從第幾個字元開始:擷取幾個字元}
unset 變數名

互動式:
read 變數名
-p  定義提示資訊
-t  字元數(限制變數值的字元)
-s  螢幕上不顯示輸入變數值
-n  超時(預設單位為秒)(限制使用者輸入變數的超時時間)

陣列定義:
array=(var1 var2 var3 ...)
array[0]=var1
array[1]=var2
array[2]=var3
普通陣列:陣列的索引是整數

定義關聯陣列
關聯陣列:索引是字串

獲取數組裡的元素:
${array[*]}
${array[2]}
${array[@]:1:2}
${!array[@]}        獲取陣列的索引號(下標)
${#array[@]}        獲取陣列索引號的個數

定義有型別的變數:
declare
-i  [設定值]宣告變數為整數型   "+i"則是取消變數所設的屬性
-x  指定的變數會成為環境變數,可供shell以外的程式來使用。相當於用export 變數名 進行匯出
-a  指定為索引陣列(普通陣列)
-A   指定為關聯陣列

2. 迴圈語句

for:
列表迴圈、非列表迴圈、類C風格            迴圈次數已知
while:
條件為真,進入迴圈,條件為假,退出迴圈    迴圈次數跟條件有關
until:
條件為假,進入迴圈,條件為真,退出迴圈    迴圈次數跟條件有關

3. 影響shell程式的內建命令

exit            退出整個程式
break           結束當前迴圈,或跳出本層迴圈
continue     忽略本次迴圈剩餘的程式碼,直接進行下一次迴圈
shift            使位置引數向左移動,預設移動1位,可以使用shift 2 移動2位 shift n 移動n位

以下指令碼都能夠實現使用者自定義輸入數字,然後指令碼計算和:
[root@MissHou shell04]# cat shift.sh 
#!/bin/bash
sum=0
while [ $# -ne 0 ]
do
let sum=$sum+$1
shift
done
echo sum=$sum

[root@localhost ~]# bash -x shift.sh 1 3
+ sum=0
+ '[' 2 -ne 0 ']'
+ let sum=0+1
+ shift              #把引數的位置向左移動一位3成為$1
+ '[' 1 -ne 0 ']'
+ let sum=1+3
+ shift
+ '[' 0 -ne 0 ']'
+ echo sum=4
sum=4

[root@MissHou shell04]# cat for3.sh 
#!/bin/bash           #迴圈次數與使用者輸入的引數有關./for3.sh 1 2 3(迴圈三次) 1 3 5 6(迴圈四次)
sum=0
for i
do
let sum=$sum+$i
done
echo sum=$sum


:
true
false

4. 補充擴充套件expect

expect 自動應答 tcl語言

需求1:A遠端登入到server上什麼都不做

rpm -q expect  #查詢是否安裝expect外掛
yum install expect -y #先安裝expect外掛
#!/usr/bin/expect #定義直譯器 用rpm -ql expect 查詢命令路徑
# 開啟一個程式
spawn ssh [email protected]
# 捕獲相關內容
expect {
"(yes/no)?" { send "yes\r";exp_continue }  #expect為捕獲,
"password:" { send "123456\r" }
}
interact //互動

指令碼執行方式:
# ./expect1.sh
# /shell04/expect1.sh
# expect -f expect1.sh


1)定義變數
#!/usr/bin/expect
set ip 192.168.137.3    #set expect裡設定變數 set 變數名 變數值
set pass 123456
set timeout 5
spawn ssh root@$ip
expect {
"yes/no" { send "yes\r";exp_continue }
"password:" { send "$pass\r" }
}
interact



2)使用位置引數
#!/usr/bin/expect
set ip [ lindex $argv 0 ]   #$arge 引數 0 第一個引數 1 第二個引數
set pass [ lindex $argv 1 ]
set timeout 5
spawn ssh root@$ip
expect {
"yes/no" { send "yes\r";exp_continue }
"password:" { send "$pass\r" }
}
interact



需求2:A遠端登入到server上操作

#!/usr/bin/expect
set ip 192.168.137.3
set pass 123456
set timeout 5
spawn ssh root@$ip
expect {
    "yes/no" { send "yes\r";exp_continue }
    "password:" { send "$pass\r" }
}

expect "#"
send "touch /tmp/file{1..3}\r" send "date\r" send "exit\r" expect eof #程式結束符

需求3:shell指令碼和expect結合使用,在多臺伺服器上建立1個使用者

[root@server shell04]# cat ip.txt 
10.1.1.1 123456
10.1.1.2 123456


1. 迴圈
2. 登入遠端主機——>ssh——>從ip.txt檔案裡獲取IP和密碼分別賦值給兩個變數
3. 使用expect程式來解決互動問題

#!/bin/bash
# 迴圈在指定的伺服器上建立使用者和檔案
while read ip pass
do
    /usr/bin/expect <<END &>/dev/null   #這個命令遇到end結束    可以自定義 若直接<<end 那麼下邊的end必須再行首處,且end前不能有空格和特殊字元,
    spawn ssh root@$ip          #下邊的end若想要對其得用tap鍵 或者乾脆在行首(centos7適用)
    expect {
    "yes/no" { send "yes\r";exp_continue }
    "password:" { send "$pass\r" }
    }
    expect "#" { send "useradd yy1;rm -rf /tmp/*;exit\r" }
    expect eof
    END
done < ip.txt



#!/bin/bash
cat ip.txt|while read ip pass
do
        {

        /usr/bin/expect <<-HOU   #此命令遇到HOU結束
        spawn ssh root@$ip
        expect {
                "yes/no" { send "yes\r";exp_continue }
                "password:" { send "$pass\r" }
        }
        expect "#"
        send "hostname\r"
        send "exit\r"
        expect eof
        HOU

        }&
done
wait
echo "user is ok...."


或者
#!/bin/bash
while read ip pass
do
        {

        /usr/bin/expect <<-HOU
        spawn ssh root@$ip
        expect {
                "yes/no" { send "yes\r";exp_continue }
                "password:" { send "$pass\r" }
        }
        expect "#"
        send "hostname\r"
        send "exit\r"
        expect eof
        HOU

        }&
done<ip.txt
wait
echo "user is ok...."

四、綜合案例

實戰案例2

寫一個指令碼,將跳板機上yunwei使用者的公鑰推送到區域網內可以ping通的所有機器上

說明:主機和密碼檔案已經提供

10.1.1.1:123456

10.1.1.2:123456

案例分析
  • 關閉防火牆和selinux

  • 判斷ssh服務是否開啟(預設ok)

  • ==迴圈判斷給定密碼檔案裡的哪些IP是可以ping通== ip pass

  • ==判斷IP是否可以ping通——>$?—>流程控制語句==

  • ==密碼檔案裡獲取主機的IP和密碼儲存變數== ip pass

  • ==判斷公鑰是否存在—>不存在建立它==

  • ==ssh-copy-id 將跳板機上的yunwei使用者的公鑰推送到遠端主機—>expect解決互動==

  • ==將ping通的主機IP單獨儲存到一個檔案==

  • ==測試驗證==

程式碼拆分
1.判斷yunwei使用者的公鑰是否存在
[ ! -f /hoem/yunwei/.ssh/id_rsa ] && ssh-keygen -P '' -f ./id_rsa

2.獲取IP並且判斷是否可以ping通
1)主機密碼檔案ip.txt
    10.1.1.1:123456
   10.1.1.2:123456
2) 迴圈判斷主機是否ping通
    tr ':' ' ' < ip.txt|while read ip pass
    do
        ping -c1 $ip &>/dev/null
      if [ $? -eq 0 ];then
          推送公鑰
      fi
    done
   


3.非互動式推送公鑰
/usr/bin/expect <<-END &>/dev/null
        spawn ssh-copy-id root@$ip
        expect {
                "yes/no" { send "yes\r";exp_continue }
                "password:" { send "$pass\r" }
        }
        expect eof
    END
最終實現
環境準備:
jumper-server    有yunwei使用者

yunwei使用者sudo授權:
visudo
## Allow root to run any commands anywhere
root    ALL=(ALL)       ALL
yunwei  ALL=(root)      NOPASSWD:ALL,!/sbin/shutdown,!/sbin/init,!/bin/rm -rf /,!/sbin/reboot
解釋說明:
1)第一個欄位yunwei指定的是使用者:可以是使用者名稱,也可以是別名。每個使用者設定一行,多個使用者設定多行,也可以將多個使用者設定成一個別名後再進行設定。
2)第二個欄位ALL指定的是使用者所在的主機:可以是ip,也可以是主機名,表示該sudo設定只在該主機上生效,ALL表示在所有主機上都生效!限制的一般都是本機,
也就是限制使用這個檔案的主機;一般都指定為"ALL"表示所有的主機,不管檔案拷到那裡都可以用。比如:10.1.1.1=...則表示只在當前主機生效。 3)第三個欄位(root)括號裡指定的也是使用者:指定以什麼使用者身份執行sudo,即使用sudo後可以享有所有root賬號下的許可權。如果要排除個別使用者,可以在括號內設定,比如ALL=(ALL,!oracle,!pos)。 4)第四個欄位ALL指定的是執行的命令:即使用sudo後可以執行所有的命令。除了關機和刪除根內容以外;也可以設定別名。NOPASSWD: ALL表示使用sudo的不需要輸入密碼。 5)也可以授權給一個使用者組 %admin ALL=(ALL) ALL 表示admin組裡的所有成員可以在任何主機上以任何使用者身份執行任何命令 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 指令碼實現: #!/bin/bash #判斷公鑰是否存在 [ ! -f /home/yunwei/.ssh/id_rsa ] && ssh-keygen -P '' -f ~/.ssh/id_rsa #迴圈判斷主機是否ping通,如果ping通推送公鑰
IFS=: #改變預設的ifs
while read ip pass do { ping -c1 $ip &>/dev/null if [ $? -eq 0 ];then echo $ip >> ~/ip_up.txt /usr/bin/expect <<-END &>/dev/null spawn ssh-copy-id root@$ip expect { "yes/no" { send "yes\r";exp_continue } "password:" { send "$pass\r" } } expect eof END fi }& done<1.txt wait echo "公鑰已經推送完畢,正在測試...." #測試驗證 remote_ip=`tail -1 ~/ip_up.txt` ssh root@$remote_ip hostname &>/dev/null test $? -eq 0 && echo "公鑰成功推送完畢"

實戰案例3

寫一個指令碼,統計web服務的不同==連線狀態==個數

#!/bin/bash
#count_http_80_state
#統計每個狀態的個數
declare -A array1
states=`ss -ant|grep 80|cut -d' ' -f1`
for i in $states
do
        let array1[$i]++
done
#通過遍歷數組裡的索引和元素打印出來
for j in ${!array1[@]}
do
        echo $j:${array1[$j]}
done