第八章、bash腳本編程(上)
05_02_bash腳本編程之一 變量、變量類型等
shell編程: 腳本編程
語言轉換器:編譯器,解釋器
編程語言:機器語言、彙編語言、高級語言
靜態語言:編譯型語言
特點:強類型(變量)
事先轉換成可執行格式
包括:C、C++、JAVA、C#
動態語言:解釋型語言, on the fly
特點:弱類型
邊解釋邊執行
包括:PHP、SHELL、python、perl
面向過程:Shell, C
面向對象: JAVA, Python, perl, C++
變量:內存空間,命名
內存:編址的存儲單元
變量類型:事先確定數據的存儲格式和長度
字符
數值
整型
浮點型: 11.23, 1.123*10^1, 0.1123*10^2
存儲日期:
字符:2013/10/10 - 64bit
數值: 99999 - 24bit,
布爾:真、假
邏輯:1+1>2
邏輯運算:與、或、非、異或
1: 真
0: 假
1 & 0 = 0
0 & 1 = 0
0 & 0 = 0
1 & 1 = 1
或:
非:
! 真 = 假
! 假 = 真
異或:相異爲1,相同爲0
shell: 弱類型編程語言
強類型,弱類型的特點:
強:變量在使用前,必須事先聲明,甚至還需要初始化;
弱:變量用時聲明,甚至不區分類型;
變量賦值:VAR_NAME=VALUE
bash變量類型:
環境變量
本地變量(局部變量)
位置變量
特殊變量
1. 本地變量:<set> VARNAME=VALUE: 作用域爲整個bash進程;set 可以省略
2. 局部變量:local VARNAME=VALUE:作用域爲當前代碼段;
3. 環境變量:作用域爲當前shell進程及其子進程;
export VARNAME=VALUE
或
VARNAME=VALUE; export VARNAME
export:“導出”
export PATH=$PATH:/usr/local/mypath
4. 位置變量:$1, $2, ...
5. 特殊變量:$?: 上一個命令的執行狀態返回值;
程序執行,可能有兩類返回值:
1. 程序執行結果
2. 程序狀態返回代碼(0-255)
0: 正確執行
1-255:錯誤執行,1,2,127系統預留;
輸出重定向:
> 覆蓋重定向
>> 追加重定向
2> 錯誤重定向
2>>錯誤追加重定向
&> 同時重定向
/dev/null: 軟件設備, bit bucket,數據黑洞
撤消變量:
unset VARNAME
查看當shell中變量:
set:包括本地變量和環境變量
查看當前shell中的環境變量:
printenv
env
export
腳本:命令的堆砌,按實際需要,結合命令流程控制機制實現的源程序
腳本的特殊標記:
shebang: 魔數
#!/bin/bash
# 註釋行,不執行
腳本在執行時會啓動一個子shell進程;
1. 命令行中啓動的腳本會繼承當前shell環境變量;
2. 系統自動執行的腳本(非命令行啓動)就需要自我定義需要各環境變量;
05_03_bash腳本編程之二 條件判斷
練習:寫一個腳本,完成以下任務 - 答案爲addusers.sh
1、添加5個用戶, user1,..., user5
2、每個用戶的密碼同用戶名,而且要求,添加密碼完成後不顯示passwd命令的執行結果信息;
3、每個用戶添加完成後,都要顯示用戶某某已經成功添加;
#!/bin/bash
useradd user1
echo "user1" | passwd --stdin user1 &> /dev/null
echo "Add user1 successfully."
條件判斷:
如果用戶不存在
添加用戶,給密碼並顯示添加成功;
否則
顯示如果已經沒在,沒有添加;
bash中如何實現條件判斷?
條件測試類型:
整數測試
字符測試
文件測試
條件測試的表達式:
[ expression ] : 中括號和expression之間必須有空格
` expression `
test expression
整數比較:
-eq: 測試兩個整數是否相等;比如 $A -eq $B
-ne: 測試兩個整數是否不等;不等,爲真;相等,爲假;
-gt: 測試一個數是否大於另一個數;大於,爲真;否則,爲假;
-lt: 測試一個數是否小於另一個數;小於,爲真;否則,爲假;
-ge: 大於或等於
-le:小於或等於
命令間的邏輯關係:
邏輯與: &&
第一個條件爲假時,第二條件不用再判斷,最終結果已經有;
第一個條件爲真時,第二條件必須得判斷;
邏輯或: ||
如果用戶user6不存在,就添加用戶user6
! id user6 && useradd user6
id user6 || useradd user6
如果/etc/inittab文件的行數大於100,就顯示好大的文件;
[ `wc -l /etc/inittab | cut -d' ' -f1` -gt 100 ] && echo "Large file."
變量名稱:
1、只能包含字母、數字和下劃線,並且不能數字開頭;
2、不應該跟系統中已有的環境變量重名;
3、最好做到見名知義;
如果用戶存在,就顯示用戶已存在;否則,就添加此用戶;
id user1 && echo "user1 exists." || useradd user1
如果用戶不存在,就添加;否則,顯示其已經存在;
! id user1 && useradd user1 || echo "user1 exists."
如果用戶不存在,添加並且給密碼;否則,顯示其已經存在;
! id user1 && useradd user1 && echo "user1" | passwd --stdin user1|| echo "user1 exists."
練習,寫一個腳本,完成以下要求: - 答案爲adduser2.sh文件
1、添加3個用戶user1, user2, user3;但要先判斷用戶是否存在,不存在而後再添加;
2、添加完成後,顯示一共添加了幾個用戶;當然,不能包括因爲事先存在而沒有添加的;
3、最後顯示當前系統上共有多少個用戶;
練習,寫一個腳本,完成以下要求: - 答案爲third.sh文件
給定一個用戶:
1、如果其UID爲0,就顯示此爲管理員;
2、否則,就顯示其爲普通用戶;
使用if語句:
如果 UID爲0;那麼
顯示爲管理員
否則
顯示爲普通用戶
NAME=user16
USERID=`id -u $NAME`
if [ $USERID -eq 0 ]; then
echo "Admin"
else
echo "common user."
fi
NAME=user16
if [ `id -u $NAME` -eq 0 ]; then
echo "Admin"
else
echo "common user."
fi
05_04_bash腳本編程之三 條件判斷及算術運算
1. 練習:寫一個腳本 - 答案爲 bash.sh
判斷當前系統上是否有用戶的默認shell爲bash;
如果有,就顯示有多少個這類用戶;否則,就顯示沒有這類用戶;
grep "bash$" /etc/passwd &> /dev/null
RETVAL=$?
if [ $RETVAL -eq 0 ]; then
if grep "bash$" /etc/passwd &> /dev/null; then
提示:“引用”一個命令的執行結果,要使用命令引用;比如: RESAULTS=`wc -l /etc/passwd | cut -d: -f1`;
使用一個命令的執行狀態結果,要直接執行此命令,一定不能引用;比如: if id user1一句中的id命令就一定不能加引號;
如果想把一個命令的執行結果賦值給某變量,要使用命令引用,比如USERID=`id -u user1`;
如果想把一個命令的執行狀態結果保存下來,並作爲命令執行成功與否的判斷條件,則需要先執行此命令,而後引用其狀態結果,如
id -u user1
RETVAL=$?
此句絕對不可以寫爲RETVAL=`id -u user1`;
2. 練習:寫一個腳本 - 答案爲bash2.sh
判斷當前系統上是否有用戶的默認shell爲bash;
如果有,就顯示其中一個的用戶名;否則,就顯示沒有這類用戶;
3. 練習:寫一個腳本
給定一個文件,比如/etc/inittab
判斷這個文件中是否有空白行;
如果有,則顯示其空白行數;否則,顯示沒有空白行。
#!/bin/bash FILE=/etc/inittab if [ ! -e $FILE ]; then echo "No $FILE." exit 8 fi if grep "^$" $FILE &> /dev/null; then echo "Total blank lines: `grep "^$" $FILE | wc -l`." else echo "No blank line." fi
4. 練習:寫一個腳本
給定一個用戶,判斷其UID與GID是否一樣
如果一樣,就顯示此用戶爲“good guy”;否則,就顯示此用戶爲“bad guy”。
#!/bin/bash USERNAME=user1 USERID=`id -u $USERNAME` GROUPID=`id -g $USERNAME` if [ $USERID -eq $GROUPID ]; then echo "Good guy." else echo "Bad guy." fi
進一步要求:不使用id命令獲得其id號;
#!/bin/bash # USERNAME=user1 if ! grep "^$USERNAME\>" /etc/passwd &> /dev/null; then echo "No such user: $USERNAME." exit 1 fi USERID=`grep "^$USERNAME\>" /etc/passwd | cut -d: -f3` GROUPID=`grep "^$USERNAME\>" /etc/passwd | cut -d: -f4` if [ $USERID -eq $GROUPID ]; then echo "Good guy." else echo "Bad guy." fi
5. 練習:寫一個腳本
給定一個用戶,獲取其密碼警告期限;
而後判斷用戶密碼使用期限是否已經小於警告期限;
提示:計算方法,最長使用期限減去已經使用的天數即爲剩餘使用期限;
如果小於,則顯示“Warning”;否則,就顯示“OK”。
圓整:丟棄小數點後的所有內容
#!/bin/bash W=`grep "student" /etc/shadow | cut -d: -f6` S=`date +%s` T=`expr $S/86400` L=`grep "^student" /etc/shadow | cut -d: -f5` N=`grep "^student" /etc/shadow | cut -d: -f3` SY=$[$L-$[$T-$N]] if [ $SY -lt $W ]; then echo 'Warning' else echo 'OK' fi
6. 練習:寫一個腳本
判定命令歷史中歷史命令的總條目是否大於1000;如果大於,則顯示“Some command will gone.”;否則顯示“OK”。
history | tail -1 | cut -d' ' -f1
需要去掉開頭的空格,cut 無法解決,留疑
shell中如何進行算術運算:
A=3
B=6
1、let 算術運算表達式
let C=$A+$B
2、$[算術運算表達式]
C=$[$A+$B]
3、$((算術運算表達式))
C=$(($A+$B))
4、expr 算術運算表達式,表達式中各操作數及運算符之間要有空格,而且要使用命令引用
C=`expr $A + $B`
條件判斷,控制結構:
1. 單分支if語句
if 判斷條件; then
statement1
statement2
...
fi
2. 雙分支的if語句:
if 判斷條件; then
statement1
statement2
...
else
statement3
statement4
...
fi
3. 多分支的if語句:
if 判斷條件1; then
statement1
...
elif 判斷條件2; then
statement2
...
elif 判斷條件3; then
statement3
...
else
statement4
...
fi
06_01_bash腳本編程之四 整數測試及特殊變量
複習:
測試方法:
[ expression ]
` expression `
test expression
bash中常用的條件測試有三種:整數,字符串,文件
整數測試:
-gt
-le
-ne
-eq
-ge
-lt
INT1=63
INT2=77
[ $INT1 -eq $INI2 ]
[[ $INT1 -eq $INT2 ]]
test $INT1 -eq $INT2
文件測試:
-e FILE:測試文件是否存在,exist的簡寫
-f FILE: 測試文件是否爲普通文件
-d FILE: 測試指定路徑是否爲目錄
-r FILE: 測試當前用戶對指定文件是否有讀取權限;
-w FILE: ~寫權限
-x FILE: ~執行
e.g:
[ -e /etc/inittab ]
[ -x /etc/rc.d/rc.sysinit ]
練習:寫一個腳本 - 答案爲filetest2.sh
給定一個文件:
如果是一個普通文件,就顯示之;
如果是一個目錄,亦顯示之;
否則,此爲無法識別之文件;
定義腳本退出狀態碼
exit: 退出腳本
exit #
如果腳本沒有明確定義退出狀態碼,那麼,最後執行的一條命令的退出碼即爲腳本的退出狀態碼;
測試腳本是否有語法錯誤:
bash -n 腳本
調試腳本:
bash -x 腳本:打印每步執行
bash變量的類型:
本地變量(局部變量)
環境變量
位置變量:
$1, $2, ...
shift [n]:移出前面的n(默認爲1)個參數,後面的參數向前移
特殊變量:
$?
$#:參數的個數
$*: 參數列表
$@:參數列表
e.g:
./filetest.sh /etc/fstab /etc/inittab
$1: /etc/fstab
$2: /etc/inittab
練習:寫一腳本 - 答案爲 filetest3.sh
能接受一個參數(文件路徑)
判定:此參數如果是一個存在的文件,就顯示“OK.”;否則就顯示"No such file."
練習:寫一個腳本 - 答案爲 cacl.sh
給腳本傳遞兩個參數(整數);
顯示此兩者之和,之乘積;
#!/bin/bash
#
if [ $# -lt 2 ]; then
echo "Usage: cacl.sh ARG1 ARG2"
exit 8
fi
echo "The sum is: $[$1+$2]."
echo "The prod is: $[$1*$2]."
練習:寫一個腳本,完成以下任務
1、使用一個變量保存一個用戶名;
2、刪除此變量中的用戶,且一併刪除其家目錄;
3、顯示“用戶刪除完成”類的信息;
#!/bin/bash
#
USER=user1
if userdel -r $USER; then
echo "Delete user finished."
else
echo "Delete user failed."
fi
06_03_bash腳本編程之五 字符串測試及for循環
shell編程引用變量:${VARNAME}, 括號有時可省略。
1. 練習: - 答案爲testuser.sh
傳遞一個用戶名參數給腳本,判斷此用戶的用戶名跟其基本組的組名是否一致,並將結果顯示出來。
#!/bin/bash
if [ 'id -nu $1' == 'id -gu $1' ]; then
fi
字符串測試:符號兩端要有空格
==:測試是否相等,相等爲真,不等爲假
!=:測試是否不等,不等爲真,等爲假
>:一般不用
< :一般不用
-z string: 測試指定字符串是否爲空,空則真,不空則假
-n string: 測試指定字符串是否不空,不空爲真,空則爲假
2. 練習:寫一個腳本
傳遞一個參數(單字符就行)給腳本,如參數爲q,就退出腳本;否則,就顯示用戶的參數;
3. 練習:寫一個腳本
傳遞一個參數(單字符就行)給腳本,如參數爲q、Q、quit或Quit,就退出腳本;否則,就顯示用戶的參數;
#!/bin/bash
if [ $1 = 'q' ];then
echo "Quiting..."
exit 1
elif [ $1 = 'Q' ];then
echo "Quiting..."
exit 2
elif [ $1 = 'quit' ];then
echo "Quiting..."
exit 3
elif [ $1 = 'Quit' ];then
echo "Quiting..."
exit 4
else
echo $1
fi
4. 練習:
傳遞三個參數給腳本,第一個爲整數,第二個爲算術運算符,第三個爲整數,將計算結果顯示出來,要求保留兩位精度。形如:
./calc.sh 5 / 2
提示:
scale=2保留兩位精度
echo "scale=2;111/22;" | bc
bc <<< "scale=2;111/22;"
#!/bin/bash
if [ $# -lt 3 ]; then
echo "Usage: `basename $0` num1 opt num2"
exit 1
fi
bc <<< "scale=2;$1 $2 $3"
5. 練習:
傳遞3個參數給腳本,參數均爲用戶名。將此些用戶的帳號信息提取出來後放置於/tmp/testusers.txt文件中,並要求每一行行首有行號。
6. 練習,寫一個腳本:
判斷當前主機的CPU生產商,其信息在/proc/cpuinfo文件中vendor_id一行中。
如果其生產商爲AuthenticAMD,就顯示其爲AMD公司;
如果其生產商爲GenuineIntel,就顯示其爲Intel公司;
否則,就說其爲非主流公司;
#!/bin/bash
VENDOR_ID=`grep vendor_id /proc/cpuinfo | head -1 | cut -d' ' -f2`
if [ $VENDOR_ID == "AuthenticAMD" ]; then
echo "AMD company"
elif [ $VENDOR_ID == "GenuineIntel" ]; then
echo "Intel company"
fi
7. 練習,寫一個腳本:
給腳本傳遞三個整數,判斷其中的最大數和最小數,並顯示出來。
MAX=0
MAX -eq $1
MAX=$1
MAX -lt $2
MAX=$2
循環:一定要有:進入條件,退出條件
1. for
2. while
3. until
for循環語句
for 變量 in 列表; do
循環體
done
列表遍歷完成之後,退出;
e.g:
for I in 1 2 3 4 5 6 7 8 9 10; do
加法運算
done
如何生成列表:
1. {1..100}
2. seq命令:`seq [起始數 [步進長度]] 結束數`,
8. 練習:求1-100的和 - 答案爲sum.sh
declare:
-i: 將一個變量聲明爲整型,integer
-x: 將一個變量聲明爲環境變量
e.g: declare -i SUM=0
9. 寫一個腳本:
1、設定變量FILE的值爲/etc/passwd
2、依次向/etc/passwd中的每個用戶問好,並顯示對方的shell,形如:
Hello, root, your shell: /bin/bash
3、統計一共有多少個用戶
for I in `seq 1 $LINES`; do echo "Hello, `head -n $I /etc/passwd | tail -1 | cut -d: -f1`"; done
只向默認shell爲bash的用戶問聲好