概念

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