初衷
:學習shell的目的很簡單,本身常常在linux服務器上作各類操做,並且基本上是一些相同的命令操做,因此就想經過shell腳原本啓動就行,可以節省必定的開發時間,提升工做效率。html
Shell 支持如下三種定義變量的方式mysql
xub$ name=value xub$ name='value' xub$ name="value" # name 是變量名,value 是賦給變量的值。
區別
linux
若是 value 不包含任何空白符(例如空格、Tab 縮進等),那麼能夠不使用引號;sql
若是 value 包含了空白符,那麼就必須使用引號包圍起來。shell
使用單引號和使用雙引號也是有區別的 下面講。編程
注意
賦值號=
的兩邊不能有空格。bash
xub$ name="小小" #賦值 xub$ echo $name #輸出命令 xub$ 小小 #輸出
使用一個定義過的變量,只要在變量名前面加美圓符號$
便可,如:服務器
xub$ home="千島湖" xub$ echo $home 千島湖 xub$ echo ${home} 千島湖
區別
變量名外面的花括號{ }
是可選的,加不加都行,加花括號是爲了幫助解釋器識別變量的邊界,好比下面這種狀況:編程語言
xub$ name="xiaoxaio" xub$ echo "my name is $namecc " my name is #發現這裏並無輸出變量名 由於系統認爲$namecc是一個總體了
改爲函數
xub$ name="xiaoxaio" xub$ echo "my name is ${name}cc " my name is xiaoxaiocc #這就是${}的優勢
上面說了定義變量時,變量的值單引號' '
,和雙引號" "
是有區別的 ,舉例以下
xub$ sex="女" xub$ one='小小的性別是:${sex}' xub$ two="小小的性別是:${sex}" xub$ echo $one 小小的性別是:${sex} xub$ echo $two 小小的性別是:女
區別
' '
包圍變量的值時,單引號裏面是什麼就輸出什麼,即便內容中有變量和命令(命令須要反引發來)也會把它們原樣輸出。" "
包圍變量的值時,輸出時會先解析裏面的變量和命令,而不是把雙引號中的變量名和命令原樣輸出。建議
:
1)若是變量的內容是數字,那麼能夠不加引號;
2)若是真的須要原樣輸出就加單引號;
3)其餘沒有特別要求的字符串等最好都加上雙引號。
使用 readonly
命令能夠將變量定義爲只讀變量,只讀變量的值不能被改變。
xub$ name=zhangsan;readonly name;name=lisi #這裏是三條命令 用;隔開 -bash: name: readonly variable #報錯
使用unset
命令能夠刪除變量。語法:
xub$ unset variable_name
注意
unset 命令不能刪除只讀變量。
Shell 也支持將命令的執行結果賦值給變量,常見的有如下兩種方式:
xub$ name=`return` xub$ name=$(return)
第一種方式把命令用反引號(位於 Esc 鍵的下方)包圍起來,反引號和單引號很是類似,容易產生混淆,因此不推薦使用這種方式;第二種方式把命令用$()
包圍起來,區分更加明顯,因此推薦使用這種方式。
舉例1
xub$ name=$(cat name.text) #把name.text文件內容賦值給name xub$ echo $name #輸出 你們好 我叫小小
舉例2,date 命令用來得到當前的系統時間,使用命令替換能夠將它的結果賦值給一個變量。
xub$ begin_time=`date` #開始時間,使用``替換 xub$ sleep 20s #休眠20秒 xub$ finish_time=$(date) #結束時間,使用$()替換 xub$ echo "Begin time: $begin_time" Begin time: 2019年 5月16日 星期四 22時37分46秒 CST xub$ echo "Finish time: $finish_time" Finish time: 2019年 5月16日 星期四 22時38分06秒 CST
使用 data 命令的%s
格式控制符能夠獲得當前的 UNIX 時間戳,這樣就能夠直接計算腳本的運行時間了。
xub$ begin_time=`date +%s` #開始時間,使用``替換 xub$ sleep 5s #休眠5秒 xub$ finish_time=$(date +%s) #結束時間,使用$()替換 xub$ run_time=$((finish_time - begin_time)) #時間差 xub$ echo "begin time: $begin_time" begin time: 1558017925 xub$ echo "finish time: $finish_time" finish time: 1558017930 xub$ echo "run time: ${run_time}s" run time: 5s
注意
:若是被替換的命令的輸出內容包括多行(也即有換行符),或者含有多個連續的空白符,那麼在輸出變量時應該將變量用雙引號包圍,不然系統會使用默認的空白符來填充,這會致使換行無效,以及連續的空白符被壓縮成一個。請看下面的代碼:
xub$ ls=`ls -l | grep damo` xub$ echo $ls #不使用雙引號包圍 drwxr-xr-x 16 xub staff 544 5 15 14:27 adamo drwxr-xr-x 4 xub staff 136 3 21 17:27 damo xub$ echo "$ls" #使用雙引號包圍 drwxr-xr-x 16 xub staff 544 5 15 14:27 adamo #發現使用雙引號纔會將變量內容分行 drwxr-xr-x 4 xub staff 136 3 21 17:27 damo
總結
原則上講,上面提到的兩種變量替換的形式是等價的,能夠隨意使用;可是,反引號畢竟看起來像單引號,有時候會對查看代碼形成困擾,而使用 $() 就相對清晰,能有效避免這種混亂。並且有些狀況必須使用它,由於它支持嵌套,反引號不行。同時也要注意$() 僅在 Bash Shell 中有效,而反引號可在多種 Shell 中使用。
運行 Shell 腳本文件時咱們能夠給它傳遞一些參數,這些參數在腳本文件內部可使用$n
的形式來接收,例如,$1 表示第一個參數,$2 表示第二個參數,依次類推。
這種經過$n
的形式來接收的參數,在 Shell 中稱爲位置參數。
請編寫下面的代碼,並命名爲family.sh
#!/bin/bash # $1表明接收第一個傳進來的參數,$2表明第二個 echo "name: $1" echo "sex: $2"
運行family.sh
xub$ sh family.sh 小小 3歲 #傳入兩個參數 中間以空格分開 name: 小小 #輸出 sex: 3歲
一樣建立family.sh腳本
#!/bin/bash #定義函數 function func(){ echo "name: ${1}" echo "age: ${2}" } #調用函數 func 小小 3歲
運行family.sh腳本
xub$ sh family.sh #運行腳本 name: 小小 #輸出 age: 3歲
注意
若是參數個數太多,達到或者超過了 10 個,那麼就得用${n}的形式來接收了,例如 ${10}、${23}。{ }的做用是爲了幫助解釋器識別參數的邊界,這跟使用變量時加{ }是同樣的效果。
變量 | 含義 |
---|---|
$0 | 當前腳本的文件名。 |
$n(n≥1) | 傳遞給腳本或函數的參數。n 是一個數字,表示第幾個參數。例如,第一個參數是 $1,第二個參數是 $2。 |
$# | 傳遞給腳本或函數的參數個數。 |
$* | 傳遞給腳本或函數的全部參數。 |
$@ | 傳遞給腳本或函數的全部參數。當被雙引號" " 包含時,$@ 與 $* 稍有不一樣,咱們將在《Shell $*和$@的區別》一節中詳細講解。 |
$? | 上個命令的退出狀態,或函數的返回值,咱們將在《Shell $?》一節中詳細講解。 |
$$ | 當前 Shell 進程 ID。對於 Shell 腳本,就是這些腳本所在的進程 ID。 |
下面咱們經過兩個例子來演示。
編寫下面的代碼,並保存爲family.sh
#!/bin/bash echo "當前shell進程 ID: $$" echo "第0個參數名稱: $0" echo "第一個參數名稱: $1" echo "第二個參數名稱: $2" echo "全部參數名稱輸出方式一: $@" echo "全部參數名稱輸出方式二: $*" echo "傳遞給腳本或函數的參數個數: $#"
運行 family.sh
xub$ sh family.sh 張三 王老五 #運行腳本 當前shell進程 ID: 38745 第0個參數名稱: family.sh 第一個參數名稱: 張三 第二個參數名稱: 王老五 全部參數名稱輸出方式一: 張三 王老五 全部參數名稱輸出方式二: 張三 王老五 傳遞給腳本或函數的參數個數: 2
編寫下面的代碼,並保存爲 family.sh
#!/bin/bash #定義函數 function fun(){ echo "當前shell進程 ID: $$" echo "第0個參數名稱: $0" echo "第一個參數名稱: $1" echo "第二個參數名稱: $2" echo "全部參數名稱輸出方式一: $@" echo "全部參數名稱輸出方式二: $*" echo "傳遞給腳本或函數的參數個數: $#" } fun 李四 趙六
運行family.sh
xub$ sh family.sh 當前shell進程 ID: 40243 第0個參數名稱: family.sh 第一個參數名稱: 李四 第二個參數名稱: 趙六 全部參數名稱輸出方式一: 李四 趙六 全部參數名稱輸出方式二: 李四 趙六 傳遞給腳本或函數的參數個數: 2
相同點
:$* 和 $@ 都表示傳遞給函數或腳本的全部參數。當 $* 和 $@ 不被雙引號" "
包圍時,它們之間沒有任何區別,都是將接收到的每一個參數看作一份數據,彼此之間以空格來分隔。
不一樣點
:可是當它們被雙引號" "包含時,就會有區別了:
"$*"
會將全部的參數從總體上看作一份數據,而不是把每一個參數都看作一份數據。"$@"
仍然將每一個參數都看做一份數據,彼此之間是獨立的。好比傳遞了 5 個參數,那麼對於"$*"
來講,這 5 個參數會合併到一塊兒造成一份數據,它們之間是沒法分割的;而對於"$@"
來講,這 5 個參數是相互獨立的,它們是 5 份數據。
若是使用 echo 直接輸出"$*"
和"$@"
作對比,是看不出區別的;但若是使用 for 循環來逐個輸出數據,當即就能看出區別來。
示例
編寫下面的代碼,並保存爲 test.sh
#!/bin/bash echo "開始遍歷參數 from \"\$*\"" for var in "$*" do echo "$var" done echo "開始遍歷參數 from \"\$@\"" for var in "$@" do echo "$var" done
運行 test.sh,並附帶參數:
xub$ sh test.sh 小小 爸爸 媽媽 開始遍歷參數 from "$*" #很明顯$*把我穿入的參數做爲一個總體 小小 爸爸 媽媽 開始遍歷參數 from "$@" # $@傳進來的參數是相互獨立的 小小 爸爸 媽媽
從運行結果能夠發現,對於"$*"
,只循環了 1 次,由於它只有 1 分數據;對於"$@"
,循環了 3 次,由於它有 3份數據。
$?
是一個特殊變量,用來獲取上一個命令的退出狀態,或者上一個函數的返回值。
所謂退出狀態,就是上一個命令執行後的返回結果。退出狀態是一個數字,通常狀況下,大部分命令執行成功會返回 0,失敗返回 1
。不過,也有一些命令返回其餘值,表示不一樣類型的錯誤。
編寫下面的代碼,並保存爲 test.sh:
#!/bin/bash if [ "$1" == 100 ] then exit 0 #參數正確,退出狀態爲0 else exit 1 #參數錯誤,退出狀態1 fi
exit
表示退出當前 Shell 進程,咱們必須在新進程中運行 test.sh,不然當前 Shell 會話(終端窗口)會被關閉,咱們就沒法取得它的退出狀態了。
例如,運行 test.sh 時傳遞參數 100:
xub$ bash ./test.sh 100 #做爲一個新進程運行 xub$ echo $? 0
再如,運行 test.sh 時傳遞參數 89:
xub$ bash ./test.sh 89 #做爲一個新進程運行 xub$ echo $? 1
編寫下面的代碼,並保存爲 test.sh:
#!/bin/bash #獲得兩個數相加的和 function add(){ return `expr $1 + $2` } add 23 50 #調用函數 echo $? #獲取函數返回值
運行結果 73
注意
:嚴格來講,Shell 函數中的 return 關鍵字用來表示函數的退出狀態,而不是函數的返回值;Shell 不像其它編程語言,沒有專門處理返回值的關鍵字。
只要本身變優秀了,其餘的事情纔會跟着好起來(少將13)