linux入門系列8--shell編程入門

本文將結合前面介紹的Linux命令、管道符等知識,通過VI編輯器編寫Shell腳本,實現能自動化工作的腳本文件。

在講解Linux常用命令“linux入門系列5--新手必會的linux命令”一文中已提到,Shell終端解釋器是人機交互的橋樑,是用戶與內核之間的翻譯官。它作爲用戶與Linux系統內部通信的媒介,爲用戶提供了一個面向Linux內核發送請求以便運行處橫向的界面系統級程序,用戶可以通過Shell啓動、掛起、停止程序。

一、Shell腳本及編程概述

1.1 Shell不僅是終端解釋器

實際上Shell不僅是一個命令行解釋器,除了能通過它控制程序的啓動、停止、掛起等操作外,它還是一個強大的編程語言,易編寫、易調試、靈活性強。Shell是解釋執行的腳本語言,可以調用Linux系統命令

Shell除了能夠支持各種變量和參數外,還提供了諸如循環、分支等高級編程語言纔有的控制特性。要想正確使用這些特性,準確下達命令尤爲重要,在講解具體編程語法之前,先學習一下Shell腳本的執行方式。

1.2 Shell腳本工作方式

Shell腳本命令的工作方式有兩種:交互式和批處理

  • 交互式:Interactive,用戶沒輸入一條命令就立即執行。到目前爲止前文采取的都是這種方式。
  • 批處理:Bach,事先編寫一個完整的Shell腳本,一次性執行腳本中的諸多指令。接下來我們將採用的就是此種方式。

通過SHELL環境變量,可以查看當前系統默認使用的終端解釋器,默認採用Bash解釋器

[root@heimatengyun ~]# echo $SHELL
/bin/bash

1.3 第一個Shell腳本

在Shell腳本中不僅會用到前文學習過的Linux命令、管道符、數據流重定向等語法規則,還需要把內部功能模塊化後通過邏輯語句進行處理,最終形成日常所見的shell腳本。

約定俗成的所有編程語言第一個程序都是Hello World,我們用Shell編寫一個簡單的腳本

[root@heimatengyun test]# ls
test1.txt  test2.txt
[root@heimatengyun test]# echo "hello world"
hello world
[root@heimatengyun test]# vi hello.sh
#!/bin/bash
#author:heimatengyun
echo " hello world"

保存並退出。Shell腳本文件可以是任意名稱,但是爲了避免被誤認爲是普通文件,建議後綴採用.sh,以表示是一個腳本文件。

ps:第一行#!/bin/bash是腳本聲明,用來告訴系統使用哪種Shell解釋器來執行腳本。

第二行#author:heimatengyun是註釋信息,對腳本功能進行介紹方便日後維護。

第三行echo " hello world"是命令語句,可以是簡單的各種linux命令,也可以是通過各種邏輯語句和語法規則組合的複雜語句塊。

Shell編程格式就這麼簡單,我稱之爲Shell編程“三段式”,以後編碼就按照這個格式編寫即可。

1.4 執行Shell腳本

Linux中執行腳本有兩種方式:用解釋器直接執行、通過腳本路徑執行

  • 用bash解釋器直接執行

語法:bash或sh 腳本名稱

案例:執行前文創建的hello.sh腳本文件

[root@heimatengyun test]# bash hello.sh 
 hello world
[root@heimatengyun test]# sh hello.sh 
 hello world
  • 通過腳本路徑執行

語法:腳本的絕對路徑或相對路徑

案例:執行前文創建的hello.sh腳本文件

[root@heimatengyun test]# /root/test/hello.sh
-bash: /root/test/hello.sh: Permission denied
[root@heimatengyun test]# ./hello.sh
-bash: ./hello.sh: Permission denied

納尼?提示權限不足。是的,你沒看錯,如果通過路徑這種方式運行,需要修改文件的執行權限,(默認的權限不可以執行,並且在數據腳本路徑時按tab鍵也無補全提示)文件權限相關命令將在後續文章中繼續講解,此處先按照如下命令添加腳本的可執行權限即可。

[root@heimatengyun test]# ll
total 12
-rw-r--r--. 1 root root  4 Dec  1 09:48 hello.sh
[root@heimatengyun test]# chmod 777 hello.sh 
[root@heimatengyun test]# ll
total 8
-rwxrwxrwx. 1 root root 53 Dec  1 09:22 hello.sh

再次執行,此時輸入路徑在按tab鍵也會提示自動補全了

[root@heimatengyun test]# ./hello.sh 
 hello world
[root@heimatengyun test]# /root/test/hello.sh 
 hello world

二、Shell編程語法

掌握Shell腳本運行方式後,我們正式開始進入Shell編程語法學習。

任何一門語言的學習都沒有速成方法,都離不開大量的練習,只有多敲才能更加熟練,才能理解的更加深刻。

2.1 變量

Linux Shell(此處爲默認bash)中的變量分爲系統變量和用戶自定義變量,系統變量包括$HOME、$PWD、$SHELL、$USER等,用戶自定義變量則爲用戶根據實際需要自定義的變量。

可以通過set命令查看Shell中所有變量。

[root@heimatengyun test]# set
BASH=/bin/bash
...省略部分內容

下文主要演示自定義變量

2.1.1 定義變量

語法:

變量名=值

變量命名規則:

​ (1)變量名可以由字母、數字和下劃線註冊,但不能以數字開頭

​ (2)等號兩側不能有空格

​ (3)變量名一般大寫

案例:

(1)定義變量並使用

[root@heimatengyun test]# SRT="wellcome"
[root@heimatengyun test]# echo $SRT
wellcome
[root@heimatengyun test]# set |grep SRT
SRT=wellcome
[root@heimatengyun test]# env |grep SRT
[root@heimatengyun test]# 

可以看到自定義變量SRT,可以通過set命令查詢出來。

(2)變量提升爲全局環境變量

[root@heimatengyun test]# export SRT
[root@heimatengyun test]# env |grep SRT
SRT=wellcome

上例中自定義的變量SRT通過env命令查看,並未在環境變量中查詢出來,通過export將自定義變量SRT提升爲環境變量後,就可以通過env查詢出來

(3)撤銷變量

[root@heimatengyun test]# unset SRT
[root@heimatengyun test]# echo $SRT

[root@heimatengyun test]# set |grep SRT
[root@heimatengyun test]# env |grep SRT

撤銷變量使用unset命令,撤銷之後變量將不存在

2.2 變量賦值

除了直接賦值,還可以將命令執行的結果賦值給變量

語法:

變量=`命令`變量=$(命令)

說明:

​ 命令用反引號或$()包含起來,先執行命令然後將命令執行結果賦值給變量。

案例:

[root@heimatengyun test]# ls
hello.sh  test1.txt  test2.txt
[root@heimatengyun test]# RESULT=`ls`
[root@heimatengyun test]# echo $RESULT
hello.sh test1.txt test2.txt
2.3 位置參數變量

位置參數變量主要用於取腳本的參數值,語法如下

變量名稱 功能
$n n爲數字,$0表示命令本身,$1-9表示第一到第九個參數,十以上的參數需要用大括號包含,如第十個參數爲${10}
$* 表示命令行中所有的參數,把參數看成一個整體
$@ 表示命令行中所有的參數,把每個參數區分對待
$# 表示命令行中所有參數的個數

案例:

輸入2個參數,計算倆個數的和並打印輸出

[root@heimatengyun test]# vi sum.sh
#sum
#!/bin/bash
#分別接收2個參數
num1=$1
num2=$2
#求和
sum=$(($num1+$num2))
#打印
echo $sum

保存並退出,執行腳本輸入2個數查看執行結果

[root@heimatengyun test]# bash sum.sh 1 2
3
2.4 預定義變量

預定義變量都有特殊的作用,參看下錶

變量名 功能
$? 表示當前進程中最後一條命令直接的返回狀態。0:執行成功;非0:執行失敗
$$ 當前進程號(PID)
$! 後臺運行的最後一個進程的進程號(PID)

案例:

(1)查看當前進程和後臺運行的最後一個進程

[root@heimatengyun test]# vi mypid.sh 
#!/bin/bash
#輸出當前進程PID,也就是當前腳本運行時生成的PID
echo "當前進程PID=$$"
echo "最後一個後臺進程PID=$!"

保存退出,執行腳本

[root@heimatengyun test]# bash mypid.sh 
當前進程PID=7810
最後一個後臺進程PID=

可以看到$$和 $!的區別,$$表示當前進程PID,而$!表示是後臺最有運行的一個進程的PID。

(2)輸出當前進程PID,並查看上一次命令執行結果

[root@heimatengyun test]# vi pid.sh
#!/bin/bash
#輸出當前進程PID,也就是當前腳本運行時生成的PID
echo "當前進程PID=$$"
#通過ls命令,查找不存在的文件,&表示讓命令後臺執行
ls -l XXX.txt&
echo "最後一個進程PID=$!"
echo "最後一條命令執行結果:$?"

保存退出,執行腳本

[root@heimatengyun test]# bash pid.sh 
當前進程PID=7395
最後一個進程PID=7396
最後一條命令執行結果:0
[root@heimatengyun test]# ls: cannot access XXX.txt: No such file or directory

[root@heimatengyun test]# 

可以看到命令執行的進程和當前腳本運行的進程不是同一個,並且雖然xxx.txt文件不存在,但結果仍然返回爲0。如果改爲查找一個已經存在的文件,毋庸置疑,返回結果肯定仍然爲0。也就是說上邊腳本不管命令執行是否成功,都將返回0,原因是通過&讓命令後臺執行,實際上是新開了一個進程,而$?只能獲取到當前進程的最後一次執行命令結果,因此在做判斷命令是否執行成功是特別要小心。

2.2 運算符和表達式

語法:

(1)$((運算式)) 或 $[運算式]

(2)expr m + n 注意expr運算符間要有空格

案例:

(1)採用$(())實現兩數相加

[root@heimatengyun test]# S=$((2+3))
[root@heimatengyun test]# echo $S
5

(2)採用$[]實現兩數相加

[root@heimatengyun test]# SUM=$[2+3]
[root@heimatengyun test]# echo $SUM 
5

(3)採用expr命令實現兩數相加

[root@heimatengyun test]# S1=`expr 2 + 3` 
[root@heimatengyun test]# echo $S1
5

注意expr命令時,操作符之間一定要有空格,否則執行的不是計算,而是字符串連接,如下示例演示了有空格和無空格的區別,另外乘法符號*由於與通配符衝突,因此需要用\轉義

[root@heimatengyun test]# expr 2 + 3
5
[root@heimatengyun test]# expr 2+3
2+3
[root@heimatengyun test]# expr 2\*3
2*3
[root@heimatengyun test]# expr 2 \* 3
6
[root@heimatengyun test]# expr 2 * 3 
expr: syntax error

(4)採用expr命令計算“2加3的和乘以5”

[root@heimatengyun test]# S2=`expr 2 + 3`
[root@heimatengyun test]# echo $S2
5
[root@heimatengyun test]# expr $S2 \* 5
25

注意操作符之間的空格,以上爲分步計算,也可以直接一步計算,多層嵌套是注意需要轉義反引號,如下:

[root@heimatengyun test]# expr `expr 2 + 3` \* 5
25
[root@heimatengyun test]# echo `expr \`expr 2 + 3\` \* 5`
25

2.3 條件判斷語句

2.3.1 條件判斷基本使用

語法:[ 條件 ]

說明:條件前後必須要有空格,非空返回true,可以使用$?驗證(0:true,非0:false)

案例:

(1)分別判斷存在和不存在的變量,檢驗返回值

[root@heimatengyun test]# [ $HOME ]  
[root@heimatengyun test]# echo $?    
0
[root@heimatengyun test]# [ $TEST ]      
[root@heimatengyun test]# echo $?    
1

由於$HOME是環境變量肯定存在,因此返回0,表示條件滿足,變量不爲空;而TEST變量由於沒定義,所以不存在,返回非0。

(2)條件判斷語句用於邏輯判斷

[root@heimatengyun test]# [ $HOME ]&& echo ok || echo notok
ok
[root@heimatengyun test]# [ $TEST ]&& echo ok || echo notok     
notok

類似於其他語言中的三元運算符,條件滿足則執行緊隨其後的語句,否則不執行。

2.3.2 常用判斷條件
  • 兩個整數之間比較
符號 含義
= 字符串比較
-lt 小於
-le 小於等於
-eq 等於
-gt 大於
-ge 大於等於
-ne 不等於
  • 按文件權限判斷
符號 含義
-r 有讀的權限
-w 有寫的權限
-x 有執行的權限
  • 按文件類型判斷
符號 含義
-f 文件存在且是一個常規文件
-d 文件存在並且是一個目錄
-e 文件存在
  • 案例

(1)整數比較

[root@heimatengyun test]# [ 1 -gt 2 ]
[root@heimatengyun test]# echo $?    
1

1不大於2,所以輸出1,表示false

(2)文件類型判斷

[root@heimatengyun test]# [ -f test1.txt ]
[root@heimatengyun test]# echo $?
0
[root@heimatengyun test]# [ -f xxxx.txt ]     
[root@heimatengyun test]# echo $?        
1

test1.txt文件存在,輸出0表示true;而xxxx.txt文件不存在,所以輸出1表示false

(3)文件權限判斷

[root@heimatengyun test]# ll
-rw-r--r--. 1 root root   9 Nov 30 20:43 test1.txt
[root@heimatengyun test]# [ -x test1.txt ]
[root@heimatengyun test]# echo $?
1

由於test1.txt 無執行權限,因此返回1,表示false

2.4 流程控制語句

流程控制結構分爲:順序結構、分支結構、循環結構。順序結構根據語句依次順序執行,分支結構根據條件判斷運行不同的分支,循環結構則根據條件判斷是否循環執行。

2.4.1 分支語句

分支語句分爲:if判斷語句、case語句

  • if判斷

語法:


if [ 判斷條件 ]

then

​ 程序

fi


或者then寫到判斷條件之後,用逗號隔開,等效於上邊語句

if [ 判斷條件 ];then

​ 程序

fi


if判斷還可以多層嵌套,形如:

if [ 判斷條件1 ]

then

​ 程序1

elif [ 判斷條件2 ]

​ then

​ 程序2

else

​ 程序3

fi


說明:條件判斷語句中,注意前中括號與if之間必須有空格,中括號前後也必須都有空格

案例:

從鍵盤輸入年齡,根據年齡返回不同的形容語言

[root@heimatengyun test]# vim if.sh
#!/bin/bash
read -p "請輸入您的年齡:" AGE
if [ $AGE -le 18 ];then
      echo "未成年"
  elif [ $AGE -le 30 ];then
      echo "年輕氣盛"
  else
      echo "糟老頭子"
fi

使用vim編輯器編輯內容(此處使用vim是因爲vim比vi更適合在編程環境使用,有錯誤提示等功能,建議編寫腳本都採用vim),保存退出並執行腳本

[root@heimatengyun test]# bash if.sh 
請輸入您的年齡:16
未成年
[root@heimatengyun test]# bash if.sh 
請輸入您的年齡:40
糟老頭子

此處使用了read命令讀取鍵盤輸入,-p參數表示顯示提示符內容。

  • case語句

語法:


case $變量 in

​ " 值1")

​ 語句1

​ ;;

​ " 值2")

​ 語句2

​ ;;

​ *)

​ 語句

​ ;;

esac


案例:

根據傳入腳本的參數分別輸出1、2、3對應的英文

[root@heimatengyun test]#vim case.sh
#!/bin/bash
case $1 in
  1)
    echo one
    ;;
  2)
    echo two
    ;;
  3)
    echo three
    ;;
  *)
    echo "error"
    ;;
esac

保存退出並執行

[root@heimatengyun test]# bash case.sh 1
one
[root@heimatengyun test]# bash case.sh 2
two
[root@heimatengyun test]# bash case.sh 3
three
[root@heimatengyun test]# bash case.sh 8
error
2.4.2 循環語句

循環語句分爲:for循環、while循環

  • for循環語句

for循環有兩種語法格式,分別如下:

語法1:


for 變量 in 值1 值2 值3...

​ do

​ 程序

​ done


語法2:


for ((初始值;循環控制條件;變量變化))

do

​ 程序

​ done


案例:

(1)一天三次問好

[root@heimatengyun test]#vim greeting.sh 
#!/bin/bash
for time in morning afternoon evening
  do
     echo "good $time"
  done

保存並退出,執行問候腳本,輸出問候語句

[root@heimatengyun test]# bash greeting.sh 
good morning
good afternoon
good evening

(2)用for循環求1累加到5的和

[root@heimatengyun test]# vim getsum.sh
#!/bin/bash
sum=0
for ((i=0;i<=5;i++))
   do
     sum=$(($i+$sum))
   done
echo "sum=$sum"

保存退出,並執行腳本

[root@heimatengyun test]# bash getsum.sh 
sum=15
  • while循環語句

語法:


while [ 條件判斷式 ]

​ do

​ 程序

​ done


案例:

(1)用while循環求1累加到5的和

[root@heimatengyun test]# vim while.sh  
#!/bin/bash
i=1
sum=0
while [ $i -le 5 ]
    do
     sum=$[$i+$sum]
     i=$[$i+1]
    done
echo "sum=$sum" 

保存並執行

[root@heimatengyun test]# bash while.sh 
sum=15

注意:

1、是給變量賦值,左邊不用加$符號,比如i=$[$i+1]不能寫成$i=$[$i+1],否則報錯“command not found”

2、條件判斷語句注意左中括號與關鍵字之間、括號內部首位、操作符與操作數之間都有空格

3、i=$[$i+1],如果錯寫爲i=$i+1則會報錯“integer expression expected”。

2.5 函數

函數是對功能的封裝,可以提供代碼重用性。函數分爲系統函數和用戶自定義函數,對於系統函數直接拿來使用即可,自定義函數則是根據具體需求編寫。

2.5.1 系統函數

使用現有的系統函數可以提高工作效率,由於篇幅所限,只簡單介紹兩個與文件路徑相關的系統函數,具體使用方法也可以通過前面介紹的man命令來查詢函數具體的用法。

  • basename

語法:

​ basename [文件路徑或字符串] [後綴]

功能描述:

​ 刪掉所有的前綴,包括最後一個/,然後打印字符串;指定了後綴則在此基礎上再去掉後綴。

案例:

[root@heimatengyun test]# basename "sdf/sdf/sdf"
sdf
[root@heimatengyun test]# basename /root/test/test1.txt 
test1.txt
[root@heimatengyun test]# basename /root/test/test1.txt txt
test1.
[root@heimatengyun test]# basename /root/test/test1.txt .txt
test1
  • dirname

語法:

​ dirname 文件絕對路徑

功能描述:

​ 從給定的包含絕對路徑的文件名中去除文件名,然後返回生效的路徑。簡單說就是去掉非目錄部分,返回保留目錄部分,但不包含最後的/

案例:

[root@heimatengyun test]# dirname /root/test/test1.txt    
/root/test
2.5.2 自定義函數

語法:


[function] 函數名[()]

{

​ 語句;

​ [return 返回值;]

}


說明:

(1)shell是解釋執行而非編譯執行,因此語句是逐行執行的,必須在函數調用之前先聲明函數。

(2)函數返回值只能通過$?系統變量獲取。可以顯示添加return語句返回(返回值範圍爲0-255),否則將以最後一條命令運行結果作爲返回值返回。

方括號內的部分function、參數、返回值可以是可選的。function show() 和function show和show()這幾種形式都是可以的。

案例:

(2)輸入1個整數,打印出比輸入小的整數

[root@heimatengyun test]# vim function.sh  
#!/bin/bash
function printNumber()
{
  i=0;
  while [ $i -lt $1 ]
  do
     echo $i;
     i=$[$i+1];
#    i=`expr \`expr $i + 1 \``     
     sleep 1;
  done
  return 0;
}
read -p "請輸入一個數:" n;
printNumber $n;

保存文件並執行腳本

[root@heimatengyun test]# bash function.sh 
請輸入一個數:5
0
1
2
3
4

三、編寫Shell腳本

通過上邊的演示,基本已經會編寫簡單的shell腳本了,本小節講解shell腳本接收用戶參數以及用戶參數的判斷。

3.1 接收用戶參數

之前提到過對linux命令來說,能否根據需要採用各種參數組合來完成特定的功能纔是衡量命令是否掌握的標準。同樣,函數或腳本也需要與用戶交互,能靈活處理用戶參數。

前面2.3中中已經提到幾個shell內設的用於接收參數的變量:$1...,$*,$@,$#

下面用案例進行演示

[root@heimatengyun test]# vim para.sh
#!/bin/bash
echo "當前腳本名稱:$0"
echo "總共有$#個參數,分別爲:$*"
echo "第一個參數爲:$1,第三個參數爲:$3"

保存腳本,並執行

[root@heimatengyun test]# bash para.sh 1 2 3
當前腳本名稱:para.sh
總共有3個參數,分別爲:1 2 3
第一個參數爲:1,第三個參數爲:3

可以看到,腳本名稱後直接帶參數,參數之間用空格隔開,在腳本內部直接可以獲取到各個位置上的參數

3.2 判斷用戶參數

shell腳本中條件判斷語句可以用來判斷表達式是否成立,若條件成立返回0表示true,否則返回其他非0隨機數表示false。按判斷對象的不同,可以分爲以下四種判斷語句:文件測試語句、邏輯測試語句、整數值比較語句、字符串比較語句。

3.2.1 文件測試語句

文件測試即是用指定的條件判斷文件是否存在或權限是否滿足,文件測試具體參數如下

運算符 作用
-d 測試文件是否爲目錄類型
-e 測試文件是否存在
-f 判斷是否爲一般文件
-r 測試當前用戶是否有讀權限
-w 測試當前用戶是否有寫權限
-x 測試當前用戶是否有執行權限

案例:

判斷是否爲目錄類型的文件

[root@heimatengyun test]# [ -d /root/test ]
[root@heimatengyun test]# echo $?
0
[root@heimatengyun test]# [ -d /root/test/test1.txt ]
[root@heimatengyun test]# echo $?
1

先通過條件測試語句進行判斷,再使用shell解釋器內置的$?變量查看執行結果。由於/root/test爲目錄,所以返回0;test1.txt是文件,所以返回1.

3.2.2 邏輯測試語句

邏輯語句用於對測試結果進行邏輯分析,根據測試結果可實現不同的效果。邏輯運算符如下:

運算符 作用
&& 邏輯與,表示當前面的命令執行成功後纔會執行後邊的命令
|| 邏輯或,表示當前面的命令執行失敗後纔會執行後邊的命令
邏輯非,表示把條件測試中判斷結果取反。

案例:

判斷當前登錄用戶是否爲管理員

[root@heimatengyun test]# [ ! $USER = root ] && echo "user" || echo "root"
root
3.2.3 整數值比較語句

整數比較運算符僅是對數字的操作,不能將數字與字符串、文件等內容一起操作,而且不能想當然地使用日常生活中的等號、大於號、小於號等來進行判斷。

ps:等號與賦值命令符衝突,大於小於號分別與輸出輸入重定向命令符衝突。因此一定要按照規範的整數比較運算符來進行操作。

可用的整數比較運算符如下表:

運算符 作用
-eq 是否等於
-ne 是否不等於
-gt 是否大於
-lt 是否小於
-le 是否小於或等於
-ge 是否大於或等於

案例:

[root@heimatengyun test]# [ 1 -lt 2 ]
[root@heimatengyun test]# echo $?    
0
[root@heimatengyun test]# [ 1 -gt 2 ]  
[root@heimatengyun test]# echo $?    
1
3.3.4 字符串比較語句

字符串比較語句用於判斷測試字符串是否爲空或兩個字符串是否相等。常用來判斷某個變量是否未被定義,即內容爲空值。字符串常見運算符如下表:

運算符 作用
= 比較字符串內容是否相同,相同爲0
!= 比較字符串內容是否不同,不同爲0
-z 判斷字符串內容是否爲空,空則爲0

案例:

分別查看存在和不存在的變量,區別返回值

[root@heimatengyun test]# echo $USER
root
[root@heimatengyun test]# echo $TEST

[root@heimatengyun test]# [ -z $USER ]
[root@heimatengyun test]# echo $?
1
[root@heimatengyun test]# [ -z $TEST ]
[root@heimatengyun test]# echo $?     
0

至此,shell編程相關知識就介紹完畢,下一篇文章繼續學習與用戶和文件、權限相關的知識。

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