02 . Shell變數和邏輯判斷及循環使用

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(root@pts/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 則為執行失敗
$#  # 位置參數總數
$*  # 所有的位置參數被看做一個字元串
$@  # 每個位置參數被看做獨立的字元串
$$  # 當前進程 PID
$!  # 上一條運行後台進程的 PID
變數引用
賦值運算符 示例
= 變數賦值
+= 兩個變數相加
自定義變數與引用
a=123
echo $a
123

a+=456
echo $a
123456

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

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

[root@redis ~]# echo ${b}123
123123


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

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

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

Shell變數除了可以直接賦值或腳本傳參外,還可以使用read命令從標準輸入獲得,read為bash內置命令,可以通過help read查看幫助

語法格式

# read [參數] [變數名]

常用參數

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

read的基本讀入

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

# read後面的參數是一個變數
[root@youmen ~]# read -p 'please input you num:' num
please input you num:234
[root@youmen ~]# echo $num
234


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

read在腳本中常用例子

[root@youmen ~]# 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
[root@youmen ~]# 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的功能
[root@youmen ~]# 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 ))"

[root@youmen ~]# 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測試表達式
# 判斷是不是文件
[root@youmen ~]# test -f /etc/hosts
[root@youmen ~]# echo $?
0
[root@youmen ~]# test -f /etc/hostss
[root@youmen ~]# echo $?
1

# 判斷文件是否可以執行
[root@youmen ~]# test -x /usr/bin/ssh
[root@youmen ~]# echo $?
0
[root@youmen ~]# test -x /etc/hosts
[root@youmen ~]# echo $?
1

# 判斷是不是目錄
[root@youmen ~]# test -d /etc/
[root@youmen ~]# echo $?
0
[root@youmen ~]# test -d /etc/hosts
[root@youmen ~]# echo $?
1
[]中括弧表達式
# 判斷是不是普通文件
[root@youmen ~]# [ -f /etc/hosts ]
[root@youmen ~]# echo $?
0
[root@youmen ~]# [ -f /etc/hostss ]
[root@youmen ~]# echo $?
1

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

# 判斷是否可被執行
[root@youmen ~]# [ -x /etc/hosts ]
[root@youmen ~]# echo $?
1
[root@youmen ~]# [ -x /usr/bin/ssh ]
[root@youmen ~]# echo $?
0
[[雙括弧表達式]]
[root@youmen ~]# [[ -x /etc/hosts ]]
[root@youmen ~]# echo $?
1
[root@youmen ~]# [[ -x /usr/bin/ssh ]]
[root@youmen ~]# echo $?
0

# [[]] 和[]一樣
# 區別是可以在多括弧裡面添加多個判斷
# 例如判斷是不是目錄,並判斷下一個文件是不是可執行
[root@youmen ~]# [[ -d /etc/ && -x /usr/bin/ssh ]]
[root@youmen ~]# echo $?
0
[root@youmen ~]# [[ -d /etc/ && -x /usr/bin/sshdd ]]
[root@youmen ~]# 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