天天看點

shell的進階上

11.1 script 的編寫與執行

shell script 其實就是純文字檔,我們可以編輯這個檔案,然後讓這個檔案來幫我們一次執行多個指令,或者是利用一些運算與邏輯判斷來幫我們達成某些功能。是以,要編輯這個檔案的内容時,當然就需要具備有 bash 指令下達的相關認識。

  • 注意事項:
  1. 指令的執行是從上而下、從左而右的分析與執行;
  2. 指令的下達就如同第四章内提到的: 指令、選項與參數間的多個空白都會被忽略掉;
  3. 空白行也将被忽略掉,并且 [tab] 按鍵所推開的空白同樣視為空格鍵;
  4. 如果讀取到一個 Enter 符号 (CR) ,就嘗試開始執行該行 (或該串) 指令;
  5. 至于如果一行的内容太多,則可以使用『 [Enter] 』來延伸至下一行;
  6. 『 # 』可做為批注!任何加在 # 後面的資料将全部被視為批注文字而被忽略!

    如此一來,我們在 script 内所撰寫的程式,就會被一行一行的執行。現在我們假設你寫的這個程式 檔案名是 /home/xiaoqi/shell.sh 那如何執行這個檔案?有底下幾個方法:

  • 直接指令下達: shell.sh 檔案必須要具備可讀與可執行 (rx) 的權限,然後:
  1. 絕對路徑:使用 /home/dmtsai/shell.sh 來下達指令;
  2. 相對路徑:假設工作目錄在 /home/dmtsai/ ,則使用 ./shell.sh 來執行
  3. 變量『PATH』功能:将 shell.sh 放在 PATH 指定的目錄内,例如: ~/bin/
  • 以 bash 程式來執行:透過『 bash shell.sh 』或『 sh shell.sh 』來執行

由于 CentOS 預設使用者家目錄下的 ~/bin 目錄會被設定到 ${PATH} 内,是以你也可以将 shell.sh 建立在 /home/xiaoqi/bin/ 底下 ( ~/bin 目錄需要自行設定) 。此時,若 shell.sh 在 ~/bin 内且具有 rx 的權限, 那就直接輸入 shell.sh 即可執行該腳本程式!

  • 那為何sh shell.sh也可以執行呢?

這是因為 /bin/sh 其實就是 /bin/bash (連結檔),使用 sh shell.sh 亦即告訴系統,我想要直接以 bash 的功能來執行 shell.sh 這個檔案内的相關指令的意思,是以此時 你的 shell.sh 隻要有 r 的權限即可被執行喔!而我們也可以利用 sh 的參數,如 -n 及 -x 來檢查與 追蹤 shell.sh 的文法是否正确!

11.2 shell寫hello world

[xiaoqi@study bin]$ mkdir bin;cd bin 
[xiaoqi@study bin]$ vim hello.sh
#!/bin/bash
#Program:
#!       This progeam shows "Hello World" in your screen.
#History
#2019/08/25     xiaoqi          First release

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin

export PATH
echo -e  "Hello World \a \n"
exit 0           

複制

請将所有編寫的 script 放置到你家目錄的 ~/bin 這個目錄内,以後比較好管理!

  • 整個程式的撰寫分成數段,大緻是這樣:
  1. 第一行 #!/bin/bash 在宣告這個 script 使用的 shell 名稱:

因為我們使用的是 bash ,是以,必須要以『 #!/bin/bash 』來宣告這個檔案内的文法使用 bash 的文法! 那麼當這個程式被執行時,他就能夠加載 bash 的相關環境配置檔案 (一般來說就是 non-login shell 的 ~/.bashrc), 并且執行 bash 來使我們底下的指令能夠執行!這很重要的!(在很多狀況中,如果沒有設定好 這一行, 那麼該程式很可能會無法執行,因為系統可能無法判斷該程式需要使用什麼 shell 來執行!

  1. 程式内容的說明:

整個 script 當中,除了第一行的『 #! 』是用來宣告 shell 的之外,其他的 # 都是『批注』用途! 是以 上面的程式當中,第二行以下就是用來說明整個程式的基本資料。一般來說, 建議你一定要養成說明該 script 的:1. 内容與功能; 2. 版本資訊; 3. 作者與聯絡方式; 4. 建立日期;5. 曆史紀錄 等等。這将有助于未來程式的改寫與 debug !

  1. 主要環境變量的宣告:

建議務必要将一些重要的環境變量設定好,個人認為,PATH 與 LANG (如果有使用到輸出相關的資訊時) 是當中最重要的!如此一來,則可讓我們這支程式在進行時,可以直接下達一些外部指令,而不必 寫絕對路徑!

  1. 主要程式部分

就将主要的程式寫好即可!在這個例子當中,就是 echo 那一行!

  1. 執行成果告知 (定義回傳值)

是否還記得之前讨論一個指令的執行成功與否,可以使用 $? 這個變量來觀察~ 那麼我們也可以利用 exit 這個指令來讓程式中斷,并且回傳一個數值給系統。在我們這個例子當中,使用 exit 0 ,這代表離開 script 并且回傳一個 0 給系統,是以我執行完這個 script 後,若接着下達 echo $? 則可得到 0 的值!利用這個 exit n (n 是數字) 的功能,我們還可以自定義錯誤訊息!

練習簡單的shell

利用 date 進行檔案的建立

假設我的伺服器内有資料庫,資料庫每天的資料都不太一樣,是以當我備份時,希望将每天的資料都備份成不同的檔名, 這樣才能夠讓舊的資料也能夠儲存下來不被覆寫。

是以我可以将檔名取成類似: backup.2019-08-16.data ,不就可以每天一個不同檔名了嗎?那個 2019-08-28 怎麼來的?那就是重點啦!接下來出個相關的例子:

假設我想要建立三個空的檔案 (透過 touch) ,檔名最開頭由使用者輸入決定, 假設使用者輸入 filename 好了,那今天的日期是 2019-08-28 , 我想要以前天、昨天、今天的日期 來建立這些檔案,亦即 filename_20190528, filename_20190527, filename_20190526 ,該如何建立呢?

[root@study xiaoqi]# vim create_3_filename.sh
#!/bin/bash


PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

#1. 讓使用者輸入檔案名,并取得filename這個變量
echo -e "I will use 'touch' command to create 3 files."
read -p "Please input your filename: " fileuser

#2. 為了避免使用者随意按 Enter ,利用變量功能分析檔名是否有設定
filename=${fileuser:-"filename"}

#3. 開始利用 date 指令來取得所需要的檔名了;
date1=$(date --date='2 days ago' +%y%m%d) #前兩天的日期
date2=$(date --date='1 days ago' +%y%m%d) # 前一天的日期
date3=$(date +%y%m%d) # 今天的日期

file1=${filename}${date1}
file2=${filename}${date2}
file3=${filename}${date3}

#4. 建立檔案
touch "${file1}"
touch "${file2}"
touch "${file3}"           

複制

『 $(command) 』的取得訊息、變量的設定功能、變量的累加以及利用 touch 指令輔助!如果你開始執行這個 create_3_filename.sh 之 後,你可以進行兩次執行:一次直接按 [Enter] 來查閱檔名是啥? 一次可以輸入一些字元,這樣可以判斷你的腳本是否設計正确!

簡單的加減乘除

使用 declare 來定義變量的類型,當變量定義成為整數後才能夠進 行加減運算!此外,我們也可以利用『 $((計算式)) 』來進行數值運算的。 可惜的是,bash shell裡頭預設僅支援到整數的資料而已。

[root@study xiaoqi]# vim multiplying.sh 
#!/bin/bash

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

echo -e "You SHOULD input 2 numbers, I will multiplying them! \n"
read -p "first number: " firstnu
read -p "second number: " secnu
total=$((${firstnu}*${secnu}))
echo -e "\nThe result of ${firstnu} x ${secnu} = ${total}"           

複制

在數值的運算上,我們可以使用『 declare -i total=${firstnu}*${secnu} 』 也可以使用上面的方式來進行!

比較建議使用這樣的方式來進行運算:

var=$((運算内容))

不但容易記憶,而且也比較友善的多,因為兩個小括号内可以加上空格符.

至于數值運算上的處理,則有:『 +, -, *, /, % 』等等。 那個 % 是取餘數!

另外,如果你想要計算含有小數點的資料時,其實可以透過 bc 這個指令的協助喔!例如可以這樣做:

[root@study xiaoqi]# echo "12.2*4.33" | bc                   
52.82           

複制

透過 bc 計算 pi

其實計算 pi 時,小數點以下位數可以無限制的延伸下去!而 bc 有提供一個運算 pi 的函式,隻是 想要使用該函式必須要使用 bc -l 來使用.也因為這個小數點以下位數可以無線延伸運算的特性存在,是以我們可以透過底下這隻小腳本來讓使用者輸入一個『小數點為數值』, 以讓 pi 能夠更準确!

[root@study xiaoqi]# vim cal_pi.sh  
#!/bim/bash

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin 
export PATH

echo -e "This program will calculate pi value. \n"
echo -e "You should input a float number to calculate pi value.\n"
read -p "The scale number (10~10000) ? " checking

num=${checking:-"10"}

echo -e "Starting calcuate pi value. Be patient."
time echo "scale=${num}; 4*a(1)" | bc -lq           

複制

上述資料中,那個 4*a(1) 是 bc 主動提供的一個計算 pi 的函數,至于 scale 就是要 bc 計算幾個 小數點下位數的意思。當 scale 的數值越大, 代表 pi 要被計算的越精确,當然用掉的時間就會越多!

11.3 script 的執行方式差異 (source, sh script, ./script)

不同的 script 執行方式會造成不一樣的結果喔!尤其影響 bash 的環境很大呢!腳本的執行方式除了 前面小節談到的方式之外,還可以利用 source 或小數點 (.) 來執行.那麼這種執行方式有何不同呢?

利用直接執行的方式來執行 script

當使用前一小節提到的直接指令下達 (不論是絕對路徑/相對路徑還是 ${PATH} 内),或者是利用 bash (或 sh) 來下達腳本時,該 script 都會使用一個新的 bash 環境來執行腳本内的指令!也就是 說,使用這種執行方式時, 其實 script 是在子程式的 bash 内執行的!我們在第十章 BASH 内談 到 export 的功能時,曾經就父程式/子程式談過一些概念性的問題, 重點在于:『當子程式完成後, 在子程式内的各項變量或動作将會結束而不會傳回到父程式中』!這是什麼意思呢?

我們舉剛剛提到過的 showname.sh 這個腳本來說明好了,這個腳本可以讓使用者自行設定兩個變量, 分别是 firstname 與 lastname,想一想,如果你直接執行該指令時,該指令幫你設定的 firstname 會 不會生效?看一下底下的執行結果:

[root@study xiaoqi]# echo ${firstname} ${lastname}
            <--變量不存在
[root@study xiaoqi]# sh showname.sh           
Please input your first name: xiao
Please input your second name: qi
You Full name is : xiaoqi        <--在 script 運作中,這兩個變量有生效
[root@study xiaoqi]# echo ${firstname} ${secname} 
        <--事實上,這兩個變量在父程式的 bash 中還是不存在的!           

複制

上面的結果你應該會覺得很奇怪,怎麼我已經利用 showname.sh 設定好的變量竟然在 bash 環境底下無效!怎麼回事呢?

如果将程式相關性繪制成圖的話,我們以下圖來說明。當你使用直接執行的 方法來處理時,系統會給予一支新的 bash 讓我們來執行 showname.sh 裡面的指令,是以你的 firstname, lastname 等變量其實是在下圖中的子程式 bash 内執行的。當 showname.sh 執行完畢後, 子程式 bash 内的所有資料便被移除,是以上表的練習中,在父程式底下 echo ${firstname} 時,就利用 source 來執行腳本:在父程式中執行如果你使用 source 來執行指令那就不一樣了!同樣的腳本我們來執行看看:看不到任何東西了!

利用 source 來執行腳本:在父程式中執行

如果你使用 source 來執行指令那就不一樣了!同樣的腳本我們來執行看看:

[root@study xiaoqi]# 
[root@study xiaoqi]# source showname.sh                  
Please input your first name: xiao
Please input your second name: qi
You Full name is : xiaoqi
[root@study xiaoqi]# echo ${firstname} ${secname}   
xiao qi        <--變量有内容           

複制

竟然生效了!沒錯!因為 source 對 script 的執行方式可以使用底下的圖示來說明! showname.sh 會在父程式中執行的,是以各項動作都會在原本的 bash 内生效!這也是為啥你不登出系統而要讓某 些寫入 ~/.bashrc 的設定生效時,需要使用

source ~/.bashrc

而不能使用

bash ~/.bashrc

是一樣的!

shell的進階上

11.4 善用判斷式

在上一章中,我們提到過 $? 這個變量所代表的意義, 此外,也透過 && 及 || 來作為前一個指令執 行回傳值對于後一個指令是否要進行的依據。如果想要判斷一個目錄是否存在, 當 時我們使用的是 ls 這個指令搭配資料流重導向,最後配合 $? 來決定後續的指令進行與否。 但是 否有更簡單的方式可以來進行『條件判斷』呢?有的~那就是『 test 』這個指令。

11.4.1 利用 test 指令的測試功能

當我要檢測系統上面某些檔案或者是相關的屬性時,利用 test 這個指令來工作真是好用得不得了, 舉例來說,我要檢查 /dmtsai 是否存在時,使用:

[root@study xiaoqi]# test -e /root/           

複制

執行結果并不會顯示任何訊息,但最後我們可以透過 $? 或 && 及 || 來展現整個結果呢! 例如我 們在将上面的例子改寫成這樣:

[root@study xiaoqi]# test -e /xiaoqi && echo "exist" || echo "Not exist"
Not exist        <--結果輸出不存在           

複制

最終的結果可以告知我們是『exist』還是『Not exist』呢!那我知道 -e 是測試一個『東西』在不在, 如果還想要測試一下該檔名是啥玩意兒時,還有哪些标志可以來判斷?有底下這些參數!

  • 關于某個檔名的『檔案類型』判斷,如 test -e filename 表示存在否
測試的标志 代表意義
-e 該『檔名』是否存在?(常用)
-f 該『檔名』是否存在且為檔案(file)?(常用)
-d 該『檔案名』是否存在且為目錄(directory)?(常用)
-b 該『檔名』是否存在且為一個 block device 裝置?
-c 該『檔名』是否存在且為一個 character device 裝置?
-S 該『檔名』是否存在且為一個 Socket 檔案?
-p 該『檔名』是否存在且為一個 FIFO (pipe) 檔案? -L 該『檔名』是否存在且為一個連結檔?
  • 關于檔案的權限偵測,如 test -r filename 表示可讀否 (但 root 權限常有例外)
測試的标志 代表意義
-r 偵測該檔名是否存在且具有『可讀』的權限?
-w 偵測該檔名是否存在且具有『可寫』的權限?
-x 偵測該檔名是否存在且具有『可執行』的權限?
-u 偵測該檔案名是否存在且具有『SUID』的屬性?
-g 偵測該檔案名是否存在且具有『SGID』的屬性?
-k 偵測該檔案名是否存在且具有『Sticky bit』的屬性?
-s 偵測該檔名是否存在且為『非空白檔案』?
  • 兩個檔案之間的比較,如: test file1 -nt file2
測試的标志 代表意義
-nt (newer than)判斷 file1 是否比 file2 新
-ot (older than)判斷 file1 是否比 file2 舊
-ef 判斷 file1 與 file2 是否為同一檔案,可用在判斷 hard link 的判定上。 主要意義在判定,兩個檔案是否均指向同一個 inode !
  • 關于兩個整數之間的判定,例如 test n1 -eq n2
測試的标志 代表意義
-eq 兩數值相等 (equal)
-ne 兩數值不等 (not equal)
-gt n1 大于 n2 (greater than)
-lt n1 小于 n2 (less than)
-ge n1 大于等于 n2 (greater than or equal)
-le n1 小于等于 n2 (less than or equal)
  • 判定字元串的資料
測試的标志 代表意義
test -z string 判定字元串是否為 0 ?若 string 為空字元串,則為 true
test -n string 判定字元串是否非為 0 ?若 string 為空字元串,則為 false。 注: -n 亦可省略
test str1 == str2 判定 str1 是否等于 str2 ,若相等,則回傳 true
test str1 != str2 判定 str1 是否不等于 str2 ,若相等,則回傳 false
  • 多重條件判定,例如: test -r filename -a -x filename
測試的标志 代表意義
-a (and)兩狀況同時成立!例如 test -r file -a -x file,則 file 同時具有 r 與 x 權限 時,才回傳 true。
-o (or)兩狀況任何一個成立!例如 test -r file -o -x file,則 file 具有 r 或 x 權限時, 就可回傳 true。
! 反相狀态,如 test ! -x file ,當 file 不具有 x 時,回傳 true
  • 現在我們就利用 test 來幫我們寫幾個簡單的例子。首先,判斷一下,讓使用者輸入一個檔名, 我們判斷:
  1. 這個檔案是否存在,若不存在則給予一個『Filename does not exist』的訊息,并中斷程式;
  2. 若這個檔案存在,則判斷他是個檔案或目錄,結果輸出『Filename is regular file』或 『Filename is directory』
  3. 判斷一下,執行者的身份對這個檔案或目錄所擁有的權限,并輸出權限資料!
[root@study xiaoqi]# cat file_perm.sh 
#!/bin/bash

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

#1. 讓使用者輸入檔案名,并且判斷使用者是否真的有輸入字元串?
echo -e "Please input a filename, I will check the filename's type and permission. \n\n" 
read -p "Input a filename : " filename
test -z ${filename} && echo "You MUST input a filename." && exit 0

#2. 判斷檔案是否存在?若不存在則顯示訊息并結束腳本
test ! -e ${filename} && echo "The filename '${filename}' DO NOT exist" && exit 0


#3. 開始判斷檔案類型與屬性
test -f ${filename} && filetype="regulare file"
test -d ${filename} && filetype="directory"
test -r ${filename} && perm="readable"
test -w ${filename} && perm="${perm} writable" 
test -x ${filename} && perm="${perm} executable"

#4. 開始輸出資訊!
echo "The filename: ${filename} is a ${filetype}" 
echo "And the permissions for you are : ${perm}"           

複制

如果你執行這個腳本後,他會依據你輸入的檔名來進行檢查喔!先看是否存在,再看為檔案或目錄類 型,最後判斷權限。 但是你必須要注意的是,由于 root 在很多權限的限制上面都是無效的,是以 使用 root 執行這個腳本時, 常常會發現與 ls -l 觀察到的結果并不相同!是以,建議使用一般使用 者來執行這個腳本試看看。

11.4.2 利用判斷符号 [ ]

除了我們很喜歡使用的 test 之外,其實,我們還可以利用判斷符号『 [ ] 』(就是中括号啦) 來進行 資料的判斷呢! 舉例來說,如果我想要知道 ${HOME} 這個變量是否為空的,可以這樣做:

[xiaoqi@study ~]$ [ -z "${HOME}" ] ; echo $?     
1           

複制

使用中括号必須要特别注意,因為中括号用在很多地方,包括通配符與正規表示法等等,是以如果要 在 bash 的文法當中使用中括号作為 shell 的判斷式時,必須要注意中括号的兩端需要有空格符來分 隔喔! 假設我空格鍵使用『□』符号來表示,那麼,在這些地方你都需要有空格鍵:

[ "$HOME" == "$MAIL" ] 
[□"$HOME"□==□"$MAIL"□]
 ↑       ↑  ↑       ↑           

複制

你會發現在上面的判斷式當中使用了兩個等号『 == 』。其實在 bash 當中使用一個等号與兩個等号的結果是一樣的! 不過在一般慣用程式的寫法中,一個等号代表『變量的設定』,兩個等号則是 代表『邏輯判斷 (是與否之意)』。 由于我們在中括号内重點在于『判斷』而非『設定變量』,是以建議還是 使用兩個等号較佳!

上面的例子在說明,兩個字元串 ${HOME} 與 ${MAIL} 是否相同的意思,相當于 test ${HOME} == ${MAIL} 的意思啦! 而如果沒有空白分隔,例如 [${HOME}==${MAIL}] 時,我們的 bash 就會顯 示錯誤訊息了!這可要很注意啊! 是以說,你最好要注意:

  • 在中括号 [] 内的每個元件都需要有空格鍵來分隔;
  • 在中括号内的變數,最好都以雙引号括号起來;
  • 在中括号内的常數,最好都以單或雙引号括号起來。

為什麼要這麼麻煩啊?直接舉例來說,假如我設定了 name="VBird Tsai" ,然後這樣判定:

[root@study xiaoqi]# [ ${name} == "Vbird" ]   
bash: [: 參數太多           

複制

怎麼會發生錯誤?bash 還跟我說錯誤是由于『太多參數 (arguments)』所緻! 為什麼呢? 因為 ${name} 如果沒有使用雙引号刮起來,那麼上面的判定式會變成:

[ VBird Tsai == "VBird" ]           

複制

上面這個是錯的.因為一個判斷式僅能有兩個資料的比對,上面 VBird 與 Tsai 還有 "VBird" 就有 三個資料! 這不是我們要的!我們要的應該是底下這個樣子:

[ "VBird Tsai" == "VBird" ]           

複制

這可是差很多的喔!另外,中括号的使用方法與 test 幾乎一模一樣啊~ 隻是中括号比較常用在條件 判斷式 if ..... then ..... fi 的情況中就是了。

那我們也使用中括号的判斷來做一個小案例好了,案 例設定如下:

  1. 當執行一個程式的時候,這個程式會讓使用者選擇 Y 或 N ,
  2. 如果使用者輸入 Y 或 y 時,就顯示『 OK, continue 』
  3. 如果使用者輸入 n 或 N 時,就顯示『 Oh, interrupt !』
  4. 如果不是 Y/y/N/n 之内的其他字元,就顯示『 I don't know what your choice is 』

利用中括号、 && 與 || 來完成!

[root@study xiaoqi]# vim ans_yn.sh 
 #!/bin/bash

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin 
export PATH

read -p "Please input (Y/N): " yn
[ "${yn}" == "Y" -o "${yn}" == "y" ] && echo "OK, continue" && exit 0
[ "${yn}" == "N" -o "${yn}" == "n" ] && echo "Oh, interrupt!" && exit 0 

echo "I don't know what your choice is" && exit 0           

複制

由于輸入正确 (Yes) 的方法有大小寫之分,不論輸入大寫 Y 或小寫 y 都是可以的,此時判斷式内 就得要有兩個判斷才行! 由于是任何一個成立即可 (大寫或小寫的 y) ,是以這裡使用 -o (或) 連結 兩個判斷!利用這個字元串判别的方法,我們就可以很輕松的将使用者想要進行的工作分門别類!

11.4.3Shell script的預設變量($0, $1...)

我們知道指令可以帶有選項與參數,例如 ls -la 可以察看包含隐藏檔案的所有屬性與權限。那麼 shell script 能不能在腳本檔名後面帶有參數呢?

舉例來說,如果你想要重新啟動系統的網絡,可以這樣做:

[root@study xiaoqi]# file /etc/init.d/network 
/etc/init.d/network: Bourne-Again shell script, ASCII text executable
#使用 file 來查詢後,系統告知這個檔案是個 bash 的可執行 script !

[root@study xiaoqi]# /etc/init.d/network restart
Restarting network (via systemctl):                        [  确定  ]           

複制

restart 是重新啟動的意思,上面的指令可以『重新啟動 /etc/init.d/network 這個程式』

那麼如果你在 /etc/init.d/network 後面加上 stop 呢?就可以直接關閉該服務了!

如果你要依據程式的執行給予一些變量去進行不同的任務時,本章一開始是使用 read 的功 能!但 read 功能的問題是你得要手動由鍵盤輸入一些判斷式。如果透過指令後面接參數, 那麼一個指令就能夠處理完畢而不需要手動再次輸入一些變量行為!這樣下達指令會比較簡單友善啦!

script 是怎麼達成這個功能的呢?其實 script 針對參數已經有設定好一些變量名稱了!對應如下:

/path/to/scriptname opt1 opt2 opt3 opt4
        $0             $1      $2   $3    $4           

複制

執行的腳本檔名為 $0 這個變量,第一個接的參數就是 $1 是以,隻要我們 在 script 裡面善用 $1 的話,就可以很簡單的立即下達某些指令功能了!

了這些數字的變量之外, 我們還有一些較為特殊的變量可以在 script 内使用來呼叫這些參數!

  • $# :代表後接的參數『個數』,以上表為例這裡顯示為『 4 』;
  • $@ :代表『 "$1" "$2" "$3" "$4" 』之意,每個變量是獨立的(用雙引号括起來);
  • $* :代表『 "$1c$2c$3c$4" 』,其中 c 為分隔字元,預設為空格鍵, 是以本例中代表『 "$1 $2 $3 $4" 』之意。

那個 $@ 與 $* 基本上還是有所不同啦!不過,一般使用情況下可以直接記憶 $@ 即可!

來做個例子吧~假設我要執行一個可以攜帶參數的 script ,執行該腳本後螢幕會顯示如下的資料:

  • 程式的檔案名為何?
  • 共有幾個參數?
  • 若參數的個數小于 2 則告知使用者參數數量太少
  • 全部的參數内容為何?
  • 第一個參數為何?
  • 第二個參數為何
[root@study xiaoqi]# vim how_paras.sh
#!/bin/bash

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin 
export PATH

echo "The script name is ==> ${0}"
echo "Total parameter number is ==> $#"
[ "$#" -lt 2 ] && echo "The number of parameter is less than 2. Stop here." && exit 0
echo "Your whole parameter is  ==> '$@'"
echo "The 1st parameter ==> ${1}"
echo "The 2nd parameter ==> ${2}"           

複制

執行結果:

[root@study xiaoqi]# sh how_paras.sh theone haha quit
The script name is ==> how_paras.sh        <--檔案名
Total parameter number is ==> 3            <--傳入的3個參數
Your whole parameter is  ==> 'theone haha quit'    <--傳入的全部參數    
The 1st parameter       ==> theone        <--第一個參數
The 2nd parameter       ==> haha        <--第二個參數           

複制

  • shift:造成參數變量号碼的便宜

除此之外,腳本後面所接的變量是否能夠進行偏移 (shift) 呢?我們直接以底下的範 例來說明好了, 用範例說明比較好解釋!我們将 how_paras.sh 的内容稍作變化一下,用來顯示每次 偏移後參數的變化情況:

[root@study xiaoqi]# cat shift_paras.sh 
#!/bin/bash

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin 
export PATH

echo "Total parameter number is ==> $#"
echo "Your whole parameter is ==> '$@'" 
shift # 進行第一次『一個變量的 shift 』 
echo "Total parameter number is ==> $#"
echo "Your whole parameter is ==> '$@'" 
shift 3 # 進行第二次『三個變量的 shift 』 
echo "Total parameter number is ==> $#" 
echo "Your whole parameter is ==> '$@'"           

複制

執行結果:

[root@study xiaoqi]# sh shift_paras.sh one two three four five six    <--給予的六個參數
Total parameter number is ==> 6        <--最原始的參數變量情況
Your whole parameter is ==> 'one two three four five six'
Total parameter number is ==> 5        <--第一次偏移,看地下發現第一個one不見了
Your whole parameter is ==> 'two three four five six'
Total parameter number is ==> 2        <--第二次便宜掉三個 two three four 不見了
Your whole parameter is ==> 'five six'           

複制

光看結果你就可以知道啦,那個 shift 會移動變量,而且 shift 後面可以接數字,代表拿掉最前面的 幾個參數的意思。 上面的執行結果中,第一次進行 shift 後他的顯示情況是『 one two three four five six』,是以就剩下五個啦!第二次直接拿掉三個,就變成『 two three four five six 』

版權屬于:龍之介大人

本文連結:https://i7dom.cn/167/2019/28/linux-bash-shell-04.html

本站所有原創文章采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協定進行許可。 您可以自由的轉載和修改,但請務必注明文章來源和作者署名并說明文章非原創且不可用于商業目的。