02 . Shell變數和邏輯判斷及迴圈使用

men 發表於 2020-06-30

Shell變數

系統變數

在命令列提示符直接執行 env、set 檢視系統或環境變數。env 顯示使用者環境變數,set 顯示 Shell預先定義好的變數以及使用者變數。可以通過 export 匯出成使用者變數。

一些寫Shell指令碼時常用的系統變數

$SHELL  預設 Shell
$HOME  當前使用者家目錄
$IFS  內部欄位分隔符
$LANG  預設語言
$PATH  預設可執行程式路徑
$PWD  當前目錄
$UID  當前使用者 ID
$USER  當前使用者
$HISTSIZE  歷史命令大小,可通過 HISTTIMEFORMAT 變數設定命令執行時間
$RANDOM  隨機生成一個 0 至 32767 的整數
$HOSTNAME  主機名
普通變數與臨時環境變數

普通變數定義:VAR=value

臨時環境變數定義:export VAR=value

變數引用:$VAR

下面看下他們之間區別:

Shell 程式的環境變數作用域是 Shell 程式,當 export 匯入到系統變數時,則作用域是 Shell 程式及其 Shell 子程式。

Shell 程式的環境變數作用域是 Shell 程式,當 export 匯入到系統變數時,則作用域是 Shell 程式及其 Shell 子程式。

ps axjf 輸出的第一列是 PPID(父程式 ID),第二列是 PID(子程式 ID)當SSH 連線 Shell 時,當前終端 PPID(-bash)是 sshd 守護程式的 PID([email protected]/0),因此在當前終端下的所有程式的 PPID 都是-bash 的 PID,比如執行命令、執行指令碼。

所以當在-bash 下設定的變數,只在-bash 程式下有效,而-bash 下的子程式 bash 是無效的,當export 後才有效。

進一步說明:再重新連線 SSH,去除上面定義的變數測試下所以在當前 shell 定義的變數一定要 export,否則在寫指令碼時,會引用不到。

還需要注意的是退出終端後,所有使用者定義的變數都會清除。

在/etc/profile 下定義的變數就是這個原理.

位置變數

位置變數指的是函式或指令碼後跟的第 n 個引數。

1−1−n,需要注意的是從第 10 個開始要用花括號呼叫,例如${10}

shift 可對位置變數控制,例如:

#!/bin/bash
echo "1: $1"
shift
echo "2: $2"
shift
echo "3: $3"

sh test.sh 1 2 3
1: 1
2: 3
3: 
# 每執行一次 shift 命令,位置變數個數就會減一,而變數值則提前一位。shift n,可設定向前移動n位。
特殊變數
$0  # 指令碼自身名字
$?  # 返回上一條命令是否執行成功,0 為執行成功,非 0 則為執行失敗
$#  # 位置引數總數
$*  # 所有的位置引數被看做一個字串
[email protected]  # 每個位置引數被看做獨立的字串
$$  # 當前程式 PID
$!  # 上一條執行後臺程式的 PID
變數引用
賦值運算子 示例
= 變數賦值
+= 兩個變數相加
自定義變數與引用
a=123
echo $a
123

a+=456
echo $a
123456

# Shell 中所有變數引用使用$符,後跟變數名

# 有時個別特殊字元會影響正常使用,就需要使用${a},例如
[[email protected] ~]# b=123
[[email protected] ~]# echo $b
123
[[email protected] ~]# echo ${b}
123
# 有時個別特殊字元會影響正常引用,那麼需要使用${VAR}
[[email protected] ~]# echo $b123

[[email protected] ~]# echo ${b}123
123123


# 將命令結果作為變數值
[[email protected] ~]# c=`echo 123`
[[email protected] ~]# echo $c
123
[[email protected] ~]# c=$(echo 123)
[[email protected] ~]# echo $c
123

# 這裡的反撇號等效於$(),都是用於執行 Shell 命令。

# 單引號和雙引號
[[email protected] ~]# d=1
[[email protected] ~]# d="1 2 $d"
[[email protected] ~]# echo $d
1 2 1
[[email protected] ~]# c=1
[[email protected] ~]# c='1 2 $c'
[[email protected] ~]# echo $c
1 2 $c
# 單引號是告訴 Shell 忽略特殊字元,而雙引號則解釋特殊符號原有的意義,比如$ 、 !。
Shell變數的輸入

Shell變數除了可以直接賦值或指令碼傳參外,還可以使用read命令從標準輸入獲得,read為bash內建命令,可以通過help read檢視幫助

語法格式

# read [引數] [變數名]

常用引數

# -p prompt: 設定提示資訊
# -t timeout: 設定輸入等待的事件,單位預設為秒

read的基本讀入

如果不加-t read就會一直等待

# read後面的引數是一個變數
[[email protected] ~]# read -p 'please input you num:' num
please input you num:234
[[email protected] ~]# echo $num
234


# 設定超時事件為3秒
read -t 3 -p "please input you num:" num
please input you num:
# 過3秒鐘會指令碼會自己執行結束

read在指令碼中常用例子

[[email protected] ~]# sh test.sh 
please input you num: 1 2
1-2 =-1
1+2 =3
1*2 =2
1/2 =0
1**2 =1
1%2 =1
[[email protected] ~]# cat abc.sh 
#!/bin/bash
read -t 18 -p "please input you num:" a b
echo "$a-$b =$(( $a - $b ))"
echo "$a+$b =$(( $a + $b ))"
echo "$a*$b =$(( $a * $b ))"
echo "$a/$b =$(( $a / $b ))"
echo "$a**$b =$(( $a ** $b ))"
echo "$a%$b =$(( $a % $b ))"

# 利用echo 命令替代和read -p的功能
[[email protected] ~]# cat test.sh 
#!/bin/bash
echo -n "請輸入兩個數字:"
read a b 
echo "$a+$b =$(( $a + $b ))"
echo "$a*$b =$(( $a * $b ))"
echo "$a/$b =$(( $a / $b ))"
echo "$a**$b =$(( $a ** $b ))"
echo "$a%$b =$(( $a % $b ))"

[[email protected] ~]# bash test.sh 
請輸入兩個數字:2 3
2+3 =5
2*3 =6
2/3 =0
2**3 =8
2%3 =2

條件測試與比較

介紹

在bash的各種流程控制結構中通常要進行各種測試,然後根據測試結果執行不同的操作,有時也會通過與if等條件語句相結合,更方便的完成判斷

條件測試通常由如下3種語法形式

# 語法1:test<測試表示式>
# 語法2:[<測試表示式>]
# 語法3:[[<測試表示式>]]

# 說明
# 1.上述語法格式1和語法格式2的寫法是相等的。語法格式3為擴充套件的test命令。推薦使用語法格式2.

# 2.在[[]]中可以使用萬用字元進行模式匹配。&&、||、>、<等操作可以應用於[[]]中,但不能應用於[]中.

# 3.對於整數的關係運算,也可以使用Shell的算術運算子(())
test測試表示式
# 判斷是不是檔案
[[email protected] ~]# test -f /etc/hosts
[[email protected] ~]# echo $?
0
[[email protected] ~]# test -f /etc/hostss
[[email protected] ~]# echo $?
1

# 判斷檔案是否可以執行
[[email protected] ~]# test -x /usr/bin/ssh
[[email protected] ~]# echo $?
0
[[email protected] ~]# test -x /etc/hosts
[[email protected] ~]# echo $?
1

# 判斷是不是目錄
[[email protected] ~]# test -d /etc/
[[email protected] ~]# echo $?
0
[[email protected] ~]# test -d /etc/hosts
[[email protected] ~]# echo $?
1
[]中括號表示式
# 判斷是不是普通檔案
[[email protected] ~]# [ -f /etc/hosts ]
[[email protected] ~]# echo $?
0
[[email protected] ~]# [ -f /etc/hostss ]
[[email protected] ~]# echo $?
1

# 判斷是否是目錄
[[email protected] ~]# [ -d /etc/hosts ]
[[email protected] ~]# echo $?
1
[[email protected] ~]# [ -d /etc/ ]
[[email protected] ~]# echo $?
0

# 判斷是否可被執行
[[email protected] ~]# [ -x /etc/hosts ]
[[email protected] ~]# echo $?
1
[[email protected] ~]# [ -x /usr/bin/ssh ]
[[email protected] ~]# echo $?
0
[[雙括號表示式]]
[[email protected] ~]# [[ -x /etc/hosts ]]
[[email protected] ~]# echo $?
1
[[email protected] ~]# [[ -x /usr/bin/ssh ]]
[[email protected] ~]# echo $?
0

# [[]] 和[]一樣
# 區別是可以在多括號裡面新增多個判斷
# 例如判斷是不是目錄,並判斷下一個檔案是不是可執行
[[email protected] ~]# [[ -d /etc/ && -x /usr/bin/ssh ]]
[[email protected] ~]# echo $?
0
[[email protected] ~]# [[ -d /etc/ && -x /usr/bin/sshdd ]]
[[email protected] ~]# echo $?
1

# &&只在雙括號裡面有效,如果單括號裡面需要使用-a,-o
檔案測試表示式
操作符 說明 舉例

# -b file 檢測檔案是否是塊裝置檔案,如果是,則返回 true。 [ -b $file ] 返回 false。

# -c file 檢測檔案是否是字元裝置檔案,如果是,則返回 true。 [ -c $file ] 返回 false。

# -d file 檢測檔案是否是目錄,如果是,則返回 true。 [ -d $file ] 返回 false。

# -f file 檢測檔案是否是普通檔案(既不是目錄,也不是裝置檔案),如果是,則返回 true。 [ -f $file ] 返回 true。 

# -g file 檢測檔案是否設定了 SGID 位,如果是,則返回 true。 [ -g $file ] 返回 false。 

# -k file 檢測檔案是否設定了粘著位(Sticky Bit),如果是,則返回 true。 [ -k $file ] 返回 false。 

# -p file 檢測檔案是否是具名管道,如果是,則返回 true。 [ -p $file ] 返回 false。

# -u file 檢測檔案是否設定了 SUID 位,如果是,則返回 true。 [ -u $file ] 返回 false。

# -r file 檢測檔案是否可讀,如果是,則返回 true。 [ -r $file ] 返回 true。

# -w file 檢測檔案是否可寫,如果是,則返回 true。 [ -w $file ] 返回 true。

# -x file 檢測檔案是否可執行,如果是,則返回 true。 [ -x $file ] 返回 true。

# -s file 檢測檔案是否為空(檔案大小是否大於0),不為空返回 true。 [ -s $file ] 返回 true。

# -e file 檢測檔案(包括目錄)是否存在,如果是,則返回 true。 [ -e $file ] 返回 true。

# 特別說明:這些操作符號對於[[]]、[]、test幾乎都是通用的,

字串表示式

字串測試操作符的作用有:比較兩個字串是否相同、字串的長度是否為零,字串是否為NULL(注:bash區分零長度字串和空字串)等

常用字串測試操作符 說明 -z “字串” 若串長度為0則真,-z可以理解為zero -n ”字串“ 若昂度不為0則真,-n 可以理解為no zero ”串1“ = ”串2“

若串1等於串2則真,可以使用”==“代替”=“

“串2” != “串2”

若串1不等於串2則真,不能用“!==“ 代替”!=“ 特別注意:

# 1. 以上表格中的字串測試操作符號務必要用”“引起來。
# 2.比較符號兩端有空格

字串測試操作符提示

# 1)-n 比較字串長度是否不為零,如果不為零則為真,如:[ -n “$myvar” ]

# 2)-z 比較字串長度是否等於零,如果等於零則為真,如:[ -z “$myvar” ]

# 特別注意
# 對於以上表格中的字串測試操作符號,如[ -n “$myvar” ],要把字串用“”引起來。

# 1、字串或字串變數比較都要加雙引號之後再比較。

# 2、字串或字串變數比較,比較符號兩端最好都有空格,可以參考系統指令碼

# “=”比較兩個字串是否相同,與“==”等價,如[ “$a” = “$b” ]其中$a這樣的變數最好用“”括起來,因為如果中間由空格,*等符號就可能出錯,更好的辦法就是[ “${a}” = “${b}” ]

# “!=” 比較兩個字串是否相同,不同則為“是”

邏輯操作符

在[]和test中使用 在[[]]中使用 說明 -a && and與,兩端都為真,則真 -o || or或,兩端有一個為真則真 ! ! not非,相反則為真

# !中文意思是反:與一個邏輯值相關的邏輯值

# -a 中文意思是(and|&&):兩個邏輯值都為“真”,返回值才為“真”,反之為“假”

# -o 中文意思是或(or| ||):兩個邏輯值只要有一個為“真”,返回值就為“真”

# 邏輯操作運算規則
# -a和&& 的運算規則:只有兩端都是1才為真
# 要想使用&&注意雙括號

Shell流程控制

If

if 語句語法格式

if condition
then
    command1 
    command2
    ...
    commandN 
fi

# 寫成一行
if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi
If else

if else語法格式

if condition
then
    command1 
    command2
    ...
    commandN
else
    command
fi
if else-if else

if else-if else語法格式

if condition1
then
    command1
elif condition2 
then 
    command2
else
    commandN
fi

例項

a=10
b=20
if [ $a == $b ]
then
   echo "a 等於 b"
elif [ $a -gt $b ]
then
   echo "a 大於 b"
elif [ $a -lt $b ]
then
   echo "a 小於 b"
else
   echo "沒有符合的條件"
fi

# a 小於 b

if else語句經常與test命令結合使用,如下所示

num1=$[2*3]
num2=$[1+5]
if test $[num1] -eq $[num2]
then
    echo '兩個數字相等!'
else
    echo '兩個數字不相等!'
fi

# 輸出結果
# 兩個數字相等!

迴圈

for

語法格式

for var in item1 item2 ... itemN
do
    command1
    command2
    ...
    commandN
done

# 寫成一行
for var in item1 item2 ... itemN; do command1; command2… done;

# 當變數值在列表裡,for迴圈即執行一次所有命令,使用變數名獲取列表中的當前取值。
# 命令可為任何有效的shell命令和語句。in列表可以包含替換、字串和檔名。
# in列表是可選的,如果不用它,for迴圈使用命令列的位置引數。
# 順序輸出當前列表中的數字

for loop in 1 2 3 4 5
do
    echo "The value is: $loop"
done

# 輸出
The value is: 1
The value is: 2
The value is: 3
The value is: 4
The value is: 5
  
# 順序輸出字串中的字元
for str in 'This is a string'
do
    echo $str
done
# 輸出結果
This is a string
While

while迴圈用於不斷執行一系列命令,也用於從輸入檔案中讀取資料;命令通常為測試條件。其格式為:

while condition
do
    command
done

一下是一個基本的while迴圈,測試條件是,如果int小於等於5,那麼條件返回真。int從0開始,每次迴圈處理時,int加1。執行上述指令碼,返回數字1到5,然後終止。

#!/bin/bash
int=1
while(( $int<=5 ))
do
    echo $int
    let "int++"
done

# 輸出
1
2
3
4
5

# 以上例項使用了 Bash let 命令,它用於執行一個或多個表示式,變數計算中不需要加上 $ 來表示變數
# while迴圈可用於讀取鍵盤資訊。下面的例子中,輸入資訊被設定為變數FILM,按<Ctrl-D>結束迴圈

echo '按下 <CTRL-D> 退出'
echo -n '輸入你最喜歡的網站名: '
while read FILM
do
    echo "是的!$FILM 是一個好網站"
done

# 執行指令碼,輸出類似下面
按下 <CTRL-D> 退出
輸入你最喜歡的網站名: youmeblog
是的!youmenblog 是一個好部落格
無限迴圈

無限迴圈語法格式

while :
do
    command
done

# or
while true
do
    command
done

# or 
for (( ; ;))
until迴圈

until 迴圈執行一系列命令直至條件為 true 時停止。

until 迴圈與 while 迴圈在處理方式上剛好相反。

一般 while 迴圈優於 until 迴圈,但在某些時候—也只是極少數情況下,until 迴圈更加有用。

until 語法格式

until condition
do
    command
done

condition 一般為條件表示式,如果返回值為 false,則繼續執行迴圈體內的語句,否則跳出迴圈。

以下例項我們使用 until 命令來輸出 0 ~ 9 的數字:

#!/bin/bash

a=0

until [ ! $a -lt 10 ]
do
   echo $a
   a=`expr $a + 1`
done

# 執行結果
0
1
2
3
4
5
6
7
8
9
case

Shell case語句為多選擇語句。可以用case語句匹配一個值與一個模式,如果匹配成功,執行相匹配的命令。case語句格式如下:

case 值 in
模式1)
    command1
    command2
    ...
    commandN
    ;;
模式2)
    command1
    command2
    ...
    commandN
    ;;
esac

case工作方式如上所示。取值後面必須為單詞in,每一模式必須以右括號結束。取值可以為變數或常數。匹配發現取值符合某一模式後,其間所有命令開始執行直至 ;;。

取值將檢測匹配的每一個模式。一旦模式匹配,則執行完匹配模式相應命令後不再繼續其他模式。如果無一匹配模式,使用星號 * 捕獲該值,再執行後面的命令。

下面的指令碼提示輸入1到4,與每一種模式進行匹配:

echo '輸入 1 到 4 之間的數字:'
echo '你輸入的數字為:'
read aNum
case $aNum in
    1)  echo '你選擇了 1'
    ;;
    2)  echo '你選擇了 2'
    ;;
    3)  echo '你選擇了 3'
    ;;
    4)  echo '你選擇了 4'
    ;;
    *)  echo '你沒有輸入 1 到 4 之間的數字'
    ;;
esac

# 輸入不同的內容,會有不同的結果,例如:
輸入 1 到 4 之間的數字:
你輸入的數字為:
3
你選擇了 3
break

break命令允許跳出所有迴圈(終止執行後面的所有迴圈)

下面的例子中,指令碼進入死迴圈直至使用者輸入數字大於5。要跳出這個迴圈,返回到shell提示符下,需要使用break命令

#!/bin/bash
while :
do
    echo -n "輸入 1 到 5 之間的數字:"
    read aNum
    case $aNum in
        1|2|3|4|5) echo "你輸入的數字為 $aNum!"
        ;;
        *) echo "你輸入的數字不是 1 到 5 之間的! 遊戲結束"
            break
        ;;
    esac
done

# 執行以上程式碼,輸出結果為
輸入 1 到 5 之間的數字:3
你輸入的數字為 3!
輸入 1 到 5 之間的數字:7
你輸入的數字不是 1 到 5 之間的! 遊戲結束
continue

continue命令與break命令類似,只有一點差別,它不會跳出所有迴圈,僅僅跳出當前迴圈。對上面的例子進行修改:

#!/bin/bash
while :
do
    echo -n "輸入 1 到 5 之間的數字: "
    read aNum
    case $aNum in
        1|2|3|4|5) echo "你輸入的數字為 $aNum!"
        ;;
        *) echo "你輸入的數字不是 1 到 5 之間的!"
            continue
            echo "遊戲結束"
        ;;
    esac
done
Case..esac

case ... esac 與其他語言中的 switch ... case 語句類似,是一種多分枝選擇結構,每個 case 分支用右圓括號開始,用兩個分號 ;; 表示 break,即執行結束,跳出整個 case ... esac 語句,esac(就是 case 反過來)作為結束標記。

case ... esac 語法格式如下

case 值 in
模式1)
    command1
    command2
    command3
    ;;
模式2)
    command1
    command2
    command3
    ;;
*)
    command1
    command2
    command3
    ;;
esac

# case後為取值,值可以為變數或常數
# 值後為關鍵字 in,接下來是匹配的各種模式,每一模式最後必須以右括號結束,模式支援正規表示式。

#!/bin/sh

site="runoob"

case "$site" in
   "runoob") echo "youmen部落格"
   ;;
   "google") echo "Google 搜尋"
   ;;
   "taobao") echo "淘寶網"
   ;;
esac