概念
shell是一種弱類型、解釋型語言,不需要編譯,只需要一個解釋器,這裏我們用bash。
輸入如下命令:
more /etc/passwd
可以看到:
當前用戶root登錄的時候,默認打開的命令行用戶接口就是bash,在這個bash中輸入bash又會打開一個子bash接口,可以不斷深入的嵌套bash,當然執行退出命令exit時,也會一層一層退出之前打開的bash。
變量類型
環境變量 當前的shell和其子shell
export 名字=值
輸入如下命令可以看到環境變量:tail -n 5 /etc/profile
腳本在執行時都會啓動一個子shell進程:
命令行中啓動的腳本會繼承當前shell環境變量。
系統自動啓動腳本(非命令行啓動):則需要自我定義環境變量。本地變量 作用於當前bash
var_name=值位置變量 用於 腳本執行的參數,$1 表示第一個參數,以此類推$1,$2….記錄參數的位置
特殊變量 bash內置的用來保存某些特殊數據的變量。(也叫系統變量)
$? 上一個命令的執行狀態返回值。
在之後shell編程中很多情況下我們只需要命令的執行狀態,不需要執行結果,所以要讓執行結果不在控制檯正常輸出,這時候需要把命令結果輸出重定向。
輸入命令:
ls -l /usr
這時候顯示結果:
現在我們要把這些命令的執行結果重定向到另一個地方,不在控制檯顯示:
命令執行正確的結果:
>覆蓋重定向 覆蓋原有內容
>> 追加重定向 在原有內容後追加
命令執行的錯誤結果:
2> 錯誤覆蓋重定向
2>>錯誤追加重定向
&> 全部重定向 包括正確和錯誤的結果
這裏要介紹一個shell編程中經常用到的重定向語句:
ls /usr &> /dev/null
null是一個設備文件,是一個字符輸出設備:
這個字符輸出設備有一個特點就是把數據重定向給他之後就再也找不回來了,相當於windows中的垃圾回收站,也叫數據黑洞。
- $# 傳遞到腳本的參數個數
- $* 傳遞到腳本的參數,與位置變量不同,此選項參數可超過9個
- $$ 腳本運行時當前進程的ID號,常用作臨時變量的後綴,如 haison.$$
- $! 後臺運行的(&)最後一個進程的ID號
- $@ 與$#相同,使用時加引號,並在引號中返回參數個數
- $- 上一個命令的最後一個參數
- $? 最後命令的退出狀態,0表示沒有錯誤,其他任何值表明有錯誤
查看變量:
set//可以查看當前bash下的所有變量
printenv//查看所有環境變量
變量的使用
${變量名},一般可以省略{},但不是所有情況都可以省略
自定義變量A=1
直接輸出變量A的時候可以省略,但是當我們想輸出變量再加一個字符串的時候,發現沒有輸出任何結果,原因是bash把Aa當成了一個變量,這個時候就需要{}來區分變量和常量字符串。
在bash中 通常有如下替換:
- 單引號:強引用,不做任何處理,只是把其中的值當做字符串
- 雙引號:弱引用,做變量替換
- 反引號:“命令替換
腳本編寫
創建後綴爲.sh的文件(沒有後綴也可以)
第一行必須寫:
#!/bin/bash //只能放第一行,稱之爲魔數
由於其起始的幾個字節的內容是固定的(或是有意填充,或是本就如此)。根據這幾個字節的內容就可以確定文件類型,因此這幾個字節的內容被稱爲魔數 (magic number)
以下是腳本編寫的練習:
寫一個腳本,完成以下任務。
1、添加5個用戶,user1,,,,user5
2、每個用戶的密碼同用戶名,要求:添加密碼完成後不顯示passwd執行結果。
3、顯示添加成功信息
1 #!/bin/bash
2 #
3 #
4 useradd $1 //添加用戶 用戶名爲傳入的第一個參數
5 echo $1 | passwd --stdin $1 &>/dev/null //使用passwd設置密碼時,會有第二遍新密碼提示輸入,作爲腳本,不可能等待用戶輸入,使用--stdin可以從標準輸出例如echo,通過管道,提前拿到默認的密碼,使其沒有第二遍的提示,然後把密碼設置成功的返回結果放入數據黑洞中
6 echo "Add User $1 success!" //打印提示信息
這個時候test.sh文件是沒有執行權限的
chmod u+x test.sh//給腳本添加執行權限
./test.sh user//腳本執行的第一種方式
sh test.sh user1//執行的第二種方式
shell中的條件判斷
表達式只能返回真或假
條件表達式:
- [ expression ] 這種更常用 表達式兩端必須有空格
- test expression
整數大小比較:
- -eq 等於 [ $1 -eq $2 ] 參數1與參數2比較是否相等
- -ne 不等於
- -gt 大於
- -ge 大於等於
- -lt 小於
- -le 小於等於
邏輯關係:在linux 中 命令執行狀態 0 爲真,其他爲假
邏輯與: &&
第一個條件爲假時,第二條件不用再判斷,最終結果已經有;
第一個條件爲真時,第二條件必須得判斷;
邏輯或: ||
第一個條件爲真時,第二條件不用再判斷,最終結果已經有;
第一個條件爲假時,第二條件必須得判斷;
邏輯非: !
還是對上一個例子加一下邏輯判斷
1 #!/bin/bash
2 [ ! $# -eq 1 ] && echo "args are error" && exit 5
3 id $1 &>/dev/null && echo "User $1 exit." && exit 2
4 id $1 &>/dev/null || useradd $1
5 id $1 &>/dev/null && echo "$1" | passwd --stdin $1 &>/dev/null && echo "Add User $1 success!"
第二行 使用了條件判斷 $# 特殊變量 獲得參數個數 如果不爲1 打印提示信息 並 退出;
第三行 id $1 查看用戶名爲$1的用戶信息 結果放入null中,若用戶存在返回狀態爲0,邏輯判斷爲真,由於是邏輯與判斷,仍然會執行後面的命令,打印用戶已存在的提示,並退出,此時可以看出通過運用邏輯與的執行特性,替代完成了if else的判斷邏輯即:用戶存在,給提示,並退出,不存在,繼續執行。
第四行代碼使用了邏輯或命令,用戶不存在 返回狀態碼非0,邏輯判斷爲假,對於邏輯或,當第一個判斷爲假的時候,仍要執行第二個邏輯判斷,所以,會執行後面的添加用戶操作。
第五行 使用邏輯與命令,當用戶存在的時候 設置密碼 並打印提示信息,用戶添加完成
接着上面的要求,添加完成之後計算系統一共有多個用戶。
1 #!/bin/bash
2 [ ! $# -eq 1 ] && echo "args are error" && exit 5
3 id $1 &>/dev/null && echo "User $1 exit." && exit 2
4 id $1 &>/dev/null || useradd $1
5 id $1 &>/dev/null && echo "$1" | passwd --stdin $1 &>/dev/null && echo "Add User $1 success!"
6 COUNT=`wc -l /etc/passwd | awk '{print $1}'`
7 echo "total Users are $COUNT"
第六行 定義一個變量 使用COUNT變量來定義用戶總數,所有的命令要用反引號“括起來,使用awk切割字符串,得到用戶數量
第七行 打印用戶數量
if判斷
If 條件 ;then
語句
elif 條件 ; then
語句
else
語句
fi
當then與if同行的時候 之間需要加分號;否則不需要加,最後結束條件判斷要加fi 代表結束。
在if判斷中的邏輯符號:
-a 與判斷
-o 或判斷
做如下例子,如果/etc/inittab文件的行數大於50,就顯示好大的文件;
1 #!/bin/bash
2
3 C=`wc -l /etc/inittab | cut -d' ' -f1`
4 if [ $C -gt 50 ];then
5 echo "big file."
6 else
7 echo "small file."
8 fi
debug 腳本
bash -n shell文件 :檢查文件是否有語法錯誤。
bash –x shell 文件 :debug 執行文件
算數表達式
- let 算術運算表達式 let C=$A + $B
- $[算術表達式] 括號中沒有空格 C = $[$A+$B]
- $((算術表達式)) C=$(($A+$B))
- expr 算術表達式 ,注意:表達式中各操作數及運算符之間要有空格。而且要使用命令引用
C=expr $A + $B
給定一個用戶,獲取其密碼警告期限,然後判斷用戶密碼使用期限是否已經小於警告期限,如果小於,則是顯示“WARN” ,否則顯示密碼還有多少天到期。
1 #!/bin/bash
2
3 if [ ! $# -eq 1 ];then
4 echo "Args errors."
5 exit 3
6 fi
7
8 U_DAY=`grep $1 /etc/shadow | cut -d: -f3`
9 M_DAY=`grep $1 /etc/shadow | cut -d: -f5`
10 W_DAY=`grep $1 /etc/shadow | cut -d: -f6`
11 N_DAY=$[`date +%s`/86400]
12 USE_DAY=$[$N_DAY-$U_DAY]
13 L_DAY=$[$M_DAY-$USE_DAY]
14
15 if [ $L_DAY -le $W_DAY ];then
16 echo "warn"
17 else
18 echo "left day is $L_DAY"
19 fi
for 循環
語法:
for 變量 in 列表 ; do
語句
done
獲得列表:
- {1..100}
- seq [起始數] [跨度數] 結束數
- ls /etc 文件列表
實現這個例子:依次向/etc/passwd中的每個用戶問好:hello 用戶名,並顯示用戶的shell:
Hello ,root ,your shell :/bin/bash。
1 #!/bin/bash
2
3 C=`wc -l /etc/passwd | cut -d' ' -f1`
4 for I in `seq $C`;do
5 UN=`head -$I /etc/passwd | tail -1 | cut -d: -f1`
6 SH=`head -$I /etc/passwd | tail -1 | cut -d: -f1`
7 echo -e "hello, $UN \t Your shell: $SH"
8 done
計算100以內所有能被3整除的整數的和
1 #!/bin/bash
2
3 SUM=0
4 I=1
5 while true;do
6 if [ $I -gt 100 ];then
7 break
8 fi
9 if [ $[$I%3] -eq 0 ];then
10 SUM=$[$SUM+$I]
11 fi
12 I=$[$I+1]
13 done
14 echo "sum=$SUM"
傳給腳本一個參數:目錄,輸出該目錄中文件最大的,文件名和文件大小:
1 #!/bin/bash
2
3 if [ ! -d $1 ];then
4 echo "Args error"
5 exit 3
6 fi
7
8 C=`du -a $1 | wc -l`
9 for I in `seq $C`;do
10 FILE_SIZE=`du -a $1 | sort -rn | head -$I | tail -1 | awk '{print $1}'`
11 FILE_NAME=`du -a $1 | sort -rn | head -$I | tail -1 | awk '{print $2}'`
12 if [ -f $FILE_NAME ];then
13 KB=$[$FILE_SIZE/1024]
14 echo "${KB}KB , $FILE_NAME"
15 break;
16 fi
17 done