主要是語法層面,不過多關注詳細命令。主要是給自己參考用。
變量
只讀變量
使用 readonly 命令可以將變量定義爲只讀變量,只讀變量的值不能被改變。
rword="hello"
echo ${rword}
readonly rword
rword="bye" #運行時報錯
刪除變量
使用 unset 命令可以刪除變量。unset 命令不能刪除只讀變量。
dword="hello"
echo ${dword}
# Output: hello
unset dword
echo ${dword}
# Output: (空)
變量類型
- 局部變量 - 局部變量只有在變量所在的代碼塊或者函數中才可見,需要使用 local 聲明;
- 全局變量 - 用戶自定義的普通變量默認是全局變量,可以在本文件中的其它位置引用;
- 環境變量 - 所有的程序(包括shell啓動的程序)都能訪問環境變量。如果一個shell腳本設置了環境變量,需要用 export 命令來通知腳本的環境。
環境變量 $RANDOM
表示一個 0 到 32767 之間的隨機整數
字符串
單引號和雙引號
shell 字符串可以用單引號 ''
,也可以用雙引號 ""
,也可以不用引號。
''
:單引號裏的任何字符都會原樣輸出,單引號中對變量引用是無效的,且單引號中不能出現單引號;""
:雙引號裏可以引用變量,可以出現轉義字符。
獲取字符串長度
text="12345"
echo ${#text}
# Output:
# 5
截取子串
${string:position} 在string中, 從位置position開始提取子串
${string:position:length} 在string中, 從位置position開始提取長度爲length的子串
查找子串
#!/usr/bin/env bash
text="hello"
echo `expr index "${text}" ll`
# Output: 3
expr 的其他幾種用法:
> expr length "this is a test"
14
> expr substr "this is a test" 3 5
is is
> expr 14 % 9
5
> expr 30 \* 3
90
刪除子串
表達式 | 含義 |
---|---|
${string#substring} | 從string的開頭,刪除最短匹配substring的子串 |
${string##substring} | 從string的開頭,刪除最長匹配substring的子串 |
${string%substring} | 從string的結尾,刪除最短匹配substring的子串 |
${string%%substring} | 從string的結尾,刪除最長匹配substring的子串 |
注:substring 可以是正則表達式。
替換子串
表達式 | 含義 |
---|---|
${string/substring/replacement} | 使用 replacement 來代替第一個匹配的 substring |
${string//substring/replacement} | 使用 replacement 代替所有匹配的 substring |
${string/#substring/replacement} | 如果 string 的前綴匹配 substring,那麼就用 replacement 來代替匹配到的 substring |
${string/%substring/replacement} | 如果 string 的後綴匹配 substring,那麼就用 replacement 來代替匹配到的 substring |
數組
bash 只支持一維數組。數組下標從 0 開始,下標可以是整數或算術表達式,其值應大於或等於 0。
創建數組
# 創建數組的不同方式
nums=([2]=2 [0]=0 [1]=1)
colors=(red yellow "dark blue")
arr_name[0]=value1
arr_name[1]=value2
arr_name[23]=value3
獲得數組長度
echo ${#nums[*]}
# Output: 3
echo ${#nums[@]}
# Output: 3
訪問數組元素
- 訪問數組的單個元素:
echo ${nums[1]}
# Output: 1
- 訪問數組的所有元素:
echo ${colors[*]}
# Output: red yellow dark blue
echo ${colors[@]}
# Output: red yellow dark blue
上面兩行有很重要(也很微妙)的區別,爲了將數組中每個元素單獨一行輸出,我們用 printf 命令:
printf "+ %s\n" ${colors[*]}
# Output:
# + red
# + yellow
# + dark
# + blue
爲什麼 dark 和 blue 各佔了一行?嘗試用引號包起來:
printf "+ %s\n" "${colors[*]}"
# Output:
# + red yellow dark blue
現在所有的元素都在一行輸出 —— 這不是我們想要的!讓我們試試 ${colors[@]}
:
printf "+ %s\n" "${colors[@]}"
# Output:
# + red
# + yellow
# + dark blue
在引號內,${colors[@]}
將數組中的每個元素擴展爲一個單獨的參數;數組元素中的空格得以保留。
- 訪問數組的部分元素:
echo ${nums[@]:0:2}
# Output: 0 1
向數組中添加元素
colors=(white "${colors[@]}" green black)
echo ${colors[@]}
# Output:
# white red yellow dark blue green black
從數組中刪除元素
用 unset 命令來從數組中刪除一個元素:
unset nums[0]
echo ${nums[@]}
# Output: 1 2
控制語句
循環控制
for 循環
for arg in elem1 elem2 ... elemN
do
# 語句
done
在每次循環的過程中,arg
依次被賦值爲從 elem1
到 elemN
。這些值還可以是通配符或者大括號擴展。
當然,我們還可以把for
循環寫在一行,但這要求do
之前要有一個分號,就像下面這樣:
for i in {1..5}; do echo $i; done
也可以像 c 語言那樣使用 for
,比如:
for (( i = 0; i < 10; i++ )); do
echo $i
done
while 循環
while [[ condition ]]
do
# 語句
done
until 循環
x=0
until [[ ${x} -ge 5 ]]; do
echo ${x}
x=`expr ${x} + 1`
done
select
select 幫助我們組織一個用戶菜單:
select answer in elem1 elem2 ... elemN
do
# 語句
done
select 會打印 elem1…elemN 以及它們的序列號到屏幕上,之後會提示用戶輸入。提示符是 $PS3
。用戶的選擇結果會被保存到answer 中。
break 和 continue
如果想提前結束一個循環或跳過某次循環執行,可以使用 shell 的 break
和 continue
語句來實現。它們可以在任何循環和 select 中使用。
函數
bash 函數定義語法如下:
[function] funname [()] {
action;
[return int;]
}
說明:
1.函數定義時,function
關鍵字可有可無。
2.函數返回值類型只能爲整數(0-255)。如果不加 return 語句,shell 默認將以最後一條命令的運行結果,作爲函數返回值。
3.函數返回值在調用該函數後通過$?
來獲得。
4.所有函數在使用前必須定義。
位置參數是在調用一個函數並傳給它參數時創建的變量。
變量 | 描述 |
---|---|
$1 … $9 |
第 1 個到第 9 個參數 |
${10} … ${N} |
第 10 個到 N 個參數 |
$* or $@ |
所有位置參數 |
$# |
位置參數的個數 |
$FUNCNAME |
函數名稱(僅在函數內部有值) |
⌨️ 示例:
calc(){
PS3="choose the oper: " # 選擇菜單的提示符
select oper in + - \* / # 生成操作符選擇菜單
do
echo -n "enter first num: " && read x # 讀取輸入參數
echo -n "enter second num: " && read y # 讀取輸入參數
case ${oper} in
"+")
return $((${x} + ${y}))
;;
"-")
return `expr ${x} - ${y}`
;;
"*")
return `expr ${x} \* ${y}`
;;
"/")
return $((${x} / ${y}))
;;
*)
echo "${oper} is not support!"
return 0
;;
esac
break
done
}
calc
echo "the result is: $?" # $? 獲取 calc 函數返回值
Shell 擴展
大括號擴展
大括號擴展讓生成任意的字符串成爲可能。它跟文件名擴展很類似,舉個例子:
echo beg{i,a,u}n # begin began begun
大括號擴展還可以用來創建一個可被循環迭代的區間:
echo {0..5} # 0 1 2 3 4 5
echo {00..8..2} # 00 02 04 06 08 00到8, 2爲步長
命令置換
命令置換允許我們對一個命令求值,並將其值置換到另一個命令或者變量賦值表達式中。當一個命令被``或$()
包圍時,命令置換將會執行。舉個例子:
now=`date +%T`
now=$(date +%T)
echo $now
算數擴展
在 bash 中,執行算數運算是非常方便的。算數表達式必須包在$(( ))
中。算數擴展的格式爲:
result=$(( ((10 + 5*3) - 7) / 2 ))
echo $result
在算數表達式中,使用變量無需帶上$
前綴:
x=4
y=7
echo $((x + y)) # 11
echo $(( ++x + y++ )) # 12
echo $((x + y)) # 13
單引號和雙引號
單引號和雙引號之間有很重要的區別。在雙引號中,變量引用或者命令置換是會被展開的。在單引號中是不會的。舉個例子:
echo "Your home: $HOME" # Your home: /Users/<username>
echo 'Your home: $HOME' # Your home: $HOME
當局部變量和環境變量包含空格時,它們在引號中的擴展要格外注意。舉個例子:
INPUT="A string with strange whitespace."
echo $INPUT # A string with strange whitespace.
echo "$INPUT" # A string with strange whitespace.
調用第一個 echo
時給了它 5 個單獨的參數 —— $INPUT
被分成了單獨的詞,echo
在每個詞之間打印了一個空格。第二種情況,調用 echo
時只給了它一個參數(整個$INPUT 的值,包括其中的空格)。
括號
小括號中的命令將會新開一個子 shell 順序執行,括號中多個命令之間用分號隔開,最後一個命令可以沒有分號,各命令和括號之間不必有空格。
[ 是 bash 的內部命令,和 test 等同。這個命令把它的參數作爲比較表達式或者作爲文件測試,並且根據比較的結果來返回一個退出狀態碼
[[ 是 bash 程序語言的關鍵字。並不是一個命令,[[ ]] 結構比 [ ] 結構更加通用。使用 [[ … ]] 條件判斷結構,而不是 [ … ],能夠防止腳本中的許多邏輯錯誤。比如,&&、||、< 和 > 操作符能夠正常存在於 [[ ]] 條件判斷結構中,但是不能出現在 [ ] 結構中。
重定向
operator | description |
---|---|
> |
重定向輸出 |
2> |
重定向錯誤輸出 |
&> |
重定向輸出和錯誤輸出 |
&>> |
以附加的形式重定向輸出和錯誤輸出 |
< |
重定向輸入 |
<< |
Here 文檔,從多行輸入 |
<<< |
Here 字符串 ,從字符串輸入 |
冒號是bash的一個內置命令,它什麼效果都沒有,<<是輸入重定向,兩個EOF(可用其它特殊成對字符替代)之間的內容通過<<輸入給冒號(:),就相當於註釋了:
:<<EOF
echo '這是多行註釋'
echo '這是多行註釋'
echo '這是多行註釋'
EOF
Debug
如果想採用 debug 模式運行某腳本,可以在其 shebang 中使用一個特殊的選項:
#!/bin/bash options
options 是一些可以改變 shell 行爲的選項。比如 -v
表示 verbose ,在執行每條命令前,向 stderr
輸出該命令 ,-x
表示 xtrace,在執行每條命令前,向 stderr
輸出該命令以及該命令的擴展參數。
有時我們值需要 debug 腳本的一部分。這種情況下,使用 set
命令會很方便。這個命令可以啓用或禁用選項。使用 -
啓用選項,+
禁用選項:
# 開啓 debug
set -x
for (( i = 0; i < 3; i++ )); do
echo ${i}
done
# 關閉 debug
set +x
for i in {1..5}; do printf ${i}; done
printf "\n"