linux-bash程式設計
目錄
1. 基本語法
1.1 註釋
註釋以"#"開始, "#"後面的內容被忽略.
說明:
- "#" 前可以沒有語句, 表示整行被註釋.
- "#" 前可以有語句, 只註釋"#"後面的內容.
1.2 變數
VAR_NAME='value' # 變數賦值, 注意等號兩邊不能有空格 echo "I am $VAR_NAME, ${VAR_NAME}" # 通過$符使用變數 LIST=$(ls) # 將shell命令結果賦值給變數, SERVER_NAME=$(hastname) # 參考子命令擴充套件
全域性變數:在指令碼中任何位置都可以使用該變數,shell變數預設都是全域性變數。
本地變數:可以在函式內部宣告本地變數,使用local關鍵字。
1.3 陣列
陣列直接賦值, 注意等號兩邊不能有空格.
$ myarr=() # 定義一個空陣列 $ myarr=(1 2 3 4 5) # 陣列元素都為數字 $ myarr=(one two three four five) # 陣列元素都為字串 $ myarr=(1 two 3 four 5) # 陣列元素由數字和字串組成 $ myarr=(1 two 3 "a line") # 陣列元素包含空格
通過陣列下標賦值
$ myarr[0]=1 # 通過下標為陣列元素賦值
$ myarr[2]=test
$ myarr[9]="a line" # 下標超出原陣列長度也可以直接賦值
獲取陣列資訊
$ myarr=(one two three four five) # 陣列元素都為字串
$
$ echo ${myarr[0]} # 通過下標訪問元素
$ echo ${myarr[@]} # 列印整個陣列
one two three four five
$ echo ${#myarr[*]} # 獲取陣列長度
$ echo ${#myarr[@]} # 同上
$ echo ${#myarr[9]} # 獲取元素的長度
$ echo ${myarr[@]:3:2} # 陣列切片, 從第3個元素開始, 元素個數為2
four five
$ myarr+=(six seven) # 給陣列追加新陣列
$ echo ${myarr[@]}
one two three four five six seven
$ unset ${myarr[9]} # 刪除陣列某個元素
$ unset myarr # 刪除整個陣列
通過變數做下標:
$ myarr=(one two three four five)
$ i=0
$ echo ${myarr[i]} # 通過變數做下標
one
$ echo ${myarr[i+1]} # 通過變數運算做下標
two
遍歷陣列: 通過in關鍵字, ${myarr[@]}加雙引號, 元素中有空格不會被拆分. 如果不加雙引號, 最後一個元素中有空格, 會被拆分.
$ myarr=(one two three four "a line")
$ for ele in "${myarr[@]}"; do echo $ele ; done
one
two
three
four
a line
$
遍歷陣列: 通過下標自增
$ myarr=(one two three four "a line")
$ for ((i=0;i<${#myarr[*]};i++)); do echo ${myarr[i]}; done
one
two
three
four
a line
$
遍歷陣列: 通過下標自增2
$ for ((i=0;i<${#myarr[*]};i+=2)); do echo "${myarr[i]}, ${myarr[i+1]}"; done
one, two
three, four
a line,
$
1.4 命令列引數
$0 # 指令碼名稱
$1 # param1
$2 # param2
...
$@ # 整個引數陣列,$1 $2 ...,不包括$0。
1.5 獲取使用者輸入
read命令接收鍵盤的輸入
$ read -p "Please Enter Your Name: " NAME
Please Enter Your Name: ZhangSan
$ echo "Your Name Is: $NAME"
Your Name Is: ZhangSan
1.6 檔案測試
注意,方括號與中間內容必須有空格。
[ -d FILE_NAME ] # FILE_NAME是dir
[ -e FILE_NAME ] # FILE_NAME存在
[ -f FILE_NAME ] # FILE_NAME存在且是regular file
[ -r FILE_NAME ] # FILE_NAME是readable
[ -s FILE_NAME ] # FILE_NAME存在且不為空
[ -w FILE_NAME ] # FILE_NAME有write permission
[ -x FILE_NAME ] # FILE_NAME是excutable
1.7 字串測試
注意,方括號與中間內容必須有空格。
[ -z STRING ] # STRING是空的
[ -n STRING ] # STRING不是空的
[ STRING1 = STRING2 ] # 字串相等
[ STRING1 != STRING2 ]# 字串不相等
1.8 算術測試
注意,方括號與中間內容必須有空格。
[ var1 -eq var2 ] # 相等
[ var1 -ne var2 ] # 不相等
[ var1 -lt var2 ] # var1 less than var2
[ var1 -le var2 ] # var1 less than or equal to var2
[ var1 -gt var2 ] # var1 greater than var2
[ var1 -ge var2 ] # var1 greater than or equal to var2
1.9 if條件判斷
語法1: 只有一個if分支
if [ condition-is-true ]
then # 注意,then不能直接寫在if語句行, 除非在if語句行尾加分號後再使用then
command 1
…
command N
fi
語法2: if帶elif分支
if [ condition-is-true ]; then
command 1
elif [ condition-is-true ]; then
command 2
elif [ condition-is-true ]; then
command 3
else
command 4
fi
1.10 case條件判斷
語法:
case "$VAR" in
pattern_1)
# commands when $VAR matches pattern 1
;;
pattern_2)
# commands when $VAR matches pattern 2
;;
*)
# This will run if $VAR doesnt match any of the given patterns
;;
esac
例子:
read -p “Enter the answer in Y/N: ” ANSWER
case “$ANSWER” in
[yY] | [yY][eE][sS])
echo “the answer is Yes.”
;;
[nN] | [nNY][oO])
echo “the answer is No.”
;;
*)
echo “invalid answer .”
;;
esac
1.11 for迴圈
語法1:
for VARIABLE_NAME in ITEM_1 ITEM_N
do
command 1
command 2
...
...
command N
done
例:
$ for V in a b c; do echo $V; done
a
b
c
$
$ COLORS="red green blue"
$ for C in $COLORS; do echo $C ; done
red
green
blue
$
FIELS=$(ls *txt)
NEW="new"
for FILE in $FILES
do
echo "Renaming $FILE to $NEW-$FILE"
mv $FILE $NEW-$FILE
done
語法2:
for (( VAR=1;VAR<N;VAR++ )) # 注意, 有兩對小括號.
do
command 1
command 2
...
...
command N
done
例
$ for ((I=0;I<5;I++)); do echo $I; done
0
1
2
3
4
1.12 while迴圈
語法:
while [ condition-is-true ]
do
command 1
command 2
…
command N
done
例: 按行讀取檔案/ect/passwd的內容
while read CURRENT_LINE; do # CURRENT_LINE是內建變數, 當前行的內容.
echo "${LINE}: $CURRENT_LINE"
((LINE++)) # 變數自增, 見模式擴充套件
done < /etc/passwd
1.13 迴圈控制
continue # 結束本次迴圈節,進入下一個迴圈節
break # 結束整個迴圈
1.14 邏輯運算 &&, ||
邏輯與: &&
邏輯或: ||
例子:建立目錄tempDir成功後,進入該目錄並建立子目錄subTempDir
mkdir tempDir && cd tempDir && mkdir subTempDir
1.15 函式
語法:
function func_name() {
command 1
command 2
...
command N
}
例子:宣告一個函式並呼叫
#!/bin/bash
function myFunc() {
echo "Shell Scripting Is Fun!"
}
myFunc # 函式呼叫
函式引數
$1 第一個引數
$2 第二個引數
$@ 引數陣列
引數之間使用空格分隔
例
#!/bin/bash
function add() {
let "SUM=$1+$2"
return $SUM
}
A=$(add 3 4)
echo "3+4=${A}"
2. 萬用字元 or 模式擴充套件
共有8種模式擴充套件,按優先順序順序分為:
序號 | 名稱 | 說明 |
---|---|---|
1 | brace expansion | 大括號 {} |
2 | tilde expansion | 波浪號 ~ |
3 | parameter and variable expansion | 引數? 變數? |
4 | arithmetic expansion | 算術擴充套件 |
5 | command substitution | 子命令擴充套件 |
6 | word splitting | 詞分割 |
7 | pathname/filename expansion | 路徑名擴充套件 |
8 | process substitution | 過程替換 |
2.1 brace expansion {}
大括號擴充套件。
有兩種形式:
- pre + {str1,str2[,…,strN]} + post
- pre + {START..END[..INCR]} + post # 擴充套件成一個序列
使用注意事項:
- 這是單純對字串的擴充套件,即使不存在對應的檔名,也會擴充套件成功。
- 逗號兩邊都不允許有空格。
- 大括號與中間內容之間不允許在空格。
- 逗號前可以為空,表示待擴充套件的為空字元,見後面例子。
- 大括號可以巢狀使用,見後面例子。
- 大括號可以與其它擴充套件聯用,見後面例子。
- START..END用於擴充套件為一個序列,一般用於迴圈。
- START..END支援逆序。
- START..END支援新增前導字元。
- START..END支援指定步長: START..END..INCR。
- START..END可以級聯使用,類似於多重迴圈。
例子 {str1,str2[,…,strN]}:
$ echo g{o,oo,ra}d
god good grad
$ cp a.log{,.bak} # 逗號前為空,擴充套件為
cp a.log a.log.bak
$ echo a{1, 2}b # 逗號兩邊有空格,不作為擴充套件處理,當作以空格分隔的兩個str。
a{1, 2}b
$ echo {j{p,pe}, pn}g # 巢狀使用大括號
jpg jpeg png
$ echo {cat, d*} # 大括號與其它擴充套件聯用
cat dog door # 注意,d*是檔名擴充套件,只有存在以d開頭的檔名時才會擴充套件成功,
# 否則,會直接將d*當作變通str而不進行擴充套件。
例子 {START..END[..INCR]}:
$ echo d{a..c}g # 普通範圍擴充套件
dag dbg dcg
$ echo Num{5..1} # 逆序擴充套件
Num5 Num4 Num3 Num2 Num1
$ echo {08..12} # 新增前導0,將所有結果擴充套件成等寬的字串
08 09 10 11 12
$ echo {08..17..2} # 對數字序列指定步長
08 10 12 14 16
$ echo {a..g..2} # 對字母序列也可以指定步長
a c e g
$ echo {x..z}{0..2}
x0 x1 x2 y0 y1 y2 z0 z1 z2
2.2 tilde expansion ~
波浪號擴充套件。
主要形式如下:
待擴充套件字元 | 擴充套件後字元 | 說明 |
---|---|---|
~ | /home/whoami |
當前使用者的主目錄 |
~username | /home/username | 特定使用者的主目錄 |
~+ | pwd |
當前路徑, 相當於pwd |
~root | /root | /root目錄 |
注意:
- ~到/之間的字元都稱為tilde-prefix。
- 波浪號用於匹配目錄,對於~username,如果username不存在該使用者,則擴充套件不成功。按原樣輸出字元。
2.3. parameter and variable expansion
引數和變數擴充套件。
兩個符號
符號 | 說明 |
---|---|
$ | 可用於引數和變數擴充套件,也可以用於命令替換和算術擴充套件。它是引數、變數、命令、算術擴充套件的“引導符” |
{} | 在引數(引數小於10時)和變數擴充套件中,{}不是必須的,但它可以起到保護變數的作用,當變數名後跟著其它字元時,用於區分變數名與字元。 |
注意:
- 使用!可以將變數的值又當作變數,多做一次擴充套件。
- 可以使用${VAR:=defaultvalue}的方式指定預設值
例子:普通變數擴充套件
$ NAME=LINUX
$ echo pre${NAME}post # 變數擴充套件,將${NAME}替換為LINUX。
preLINUXpost
$
$ echo pre$NAMEpost # 沒使用{},認為$NAMEpost是一個變數,為空,所以擴充套件為pre。
pre
例子:使用!
$ NAME=LINUX
$ LINUX=Ubuntu
$ echo pre${NAME}post
preLINUXpost
$ echo pre${!NAME}post # 加一個!,將NAME的值LINUX又當作變數名,擴充套件為Ubuntu。
preUbuntupost
例子:自定義變數預設值
$ NAME= # 清空NAME的值,由於bash未定義的變數值都是空,所以直接賦值為空。
$ echo pre{NAME:=WINDOWS}post # NAME未定義,NAME賦值為預設值WINDOWS。
preWINDOWSpost
$ echo $NAME # 在上一語句中,NAME已經被賦值為指定的預設值
WINDOWS
2.4 arithmetic expansion
算術擴充套件。
語法: $((EXPRESSION))
注意:
- (())可以進行算術運算。
- (())可以將非10進位制數轉為10進位制數。
- (())中可以有空格,也可以沒空格。
- (())中可以使用變數。
例子:
$ echo $(( 3 + 4 ))
7
$ A=3
$ echo $(($A+4))
7
$ A=$((011)); echo $A # 以0開頭的是8進位制數
9
$ A=$((0x11)); echo $A # 以0x開頭的是16進位制數
17
2.5. command substitution
命令替換。
語法:$(command) 或 command
作用:將command的輸出作為當前命令的一部分。
注意:
- \((command) 使用小括號,是命令替換,\){VAR}使用大括號是變數擴充套件。$((expr))是算術擴充套件。
- $(command) 將command的輸出作為當前命令,而不只是執行command
$ date
2020年 08月 12日 星期三 11:06:11 CST
$ $(date) # 本條命令相當於在命令列執行“2020年 08月 12日 星期三 11:06:11 CST”。
2020年: command not found
2.6 word splitting
詞分離。
如果引數擴充套件、命令替換、算術擴充套件沒有在雙引號中,shell將對其結果進行“詞分離”操作。
shell把\(IFS中的每一個字元看做是分隔符。
預設情況下\)IFS的值是“
可以修改$IFS的值。
$ IFS=’123’ # 1,2,3都作為詞分隔符
$ echo a1b2c3d4 # 在命令列中,不進行“word splitting”操作
a1b2c3d4
$ echo $(echo a1b2c3d4) # 將echo a1b2c3d4放到命令替換語句中
a b c d4 # 以1,2,3將a1b2c3d4分割為a b c d4共4個份
$ ls $(echo a1b2c3d4) # 將echo a1b2c3d4放到命令替換語句中
ls: canot access ‘a’: No such file or directory # a b c d4放到ls命令後面
ls: canot access ‘b’: No such file or directory # 相當於執行 ls a b c d4
ls: canot access ‘c’: No such file or directory #
ls: canot access ‘d4’: No such file or directory#
2.7 filename expansion
檔名擴充套件.
全為3種
副檔名 | 說明 | 例 |
---|---|---|
* | 匹配0~N個任意字元, 不能匹配空格 | ls .txt, ls . |
? | 匹配1個任意字元,不能匹配空格 | ls ?.txt |
[abc] | 匹配a、b、c中任意一個字元 | ls [ab].txt |
[^abc]或[!abd] | 匹配a、b、c以外的任意一個字元 | ls [!ab].txt |
[start-end] | 匹配連續範圍內的任意一個字元 | ls [a-c].txt |
[^start-end] | 匹配連續範圍外的任意一個字元 | ls [^1-3].txt |
注意:
- 只有對應檔名存在時才能擴充套件成功,否則原樣輸出。比如 ls *.txt如果路徑下沒有.txt檔案,則直接將 .txt作為ls的引數,會報告沒有“.txt”這個檔案。
- *和?不能匹配空格。
例子:
$ ls * # 所有檔案,但不包含隱藏檔案
$ ls .* # 所有隱藏檔案,但包含了’.’和’..’這兩個目錄
$ ls .[!.]* # 所有隱藏檔案,不包括’.’和’..’這兩個目錄
$ ls [a-c].txt # a.txt b.txt c.txt,只擴充套件為已經存在的檔案。
# 比如如果b.txt不存在,則只擴充套件為a.txt c.txt
2.8. process substitution
過程替換。
過程替換與重定向比較容易搞混:重定向是一口入一口出,過程替換是多入(實際是並沒有“入”)。看如下試驗:
$ ls <(true) # 注意,”<”和”(“之間不能有空格,否則認為是重定向,語法錯誤。
/dev/fd/63 # 可以認為<(true)就是檔案/dev/fd/63
$ vim <(ls) # 通過vim視窗底部顯示,確認檔名為/dev/fd/63.
1 a.txt
2 c.txt
3 ex.sh
“/dev/fd/63” [readonly] 3L, 18C
$ ls <(true) >(true) # 可以認為<(LIST) 和 >(LIST)分別都是一個檔名。
/dev/fd/62 /dev/fd/63
if the “>(LIST)” form is used, writing to the file will provide input for LIST.
if the “<(LIST)” form is used, the file passed as an argument should be read to obtain the output of LIST
舉例說明:
$ cat <(ls) # <(ls)是一個檔案,檔案內容是ls的結果,cat這個檔案。
$ ls > >(cat) # >(cat)是一個檔案,檔案的內容是ls重定向來的,cat這個檔案。
例子:將兩個檔案分別sort並排序,然後找出在兩個檔案中都出現的行。
$ cat a.txt
d3
d4
a0
a1
a2
$ cat b.txt
c0
c1
c2
d3
d4
$ comm <(sort -u a.txt) <(sort -u b.txt) # 給comm傳兩個檔案,檔案內容是sort -u結果。
a0 #---第一列,僅出現在第一個檔案中的行
a1
a2
c0 #---第二列,僅出現在第二個檔案中的行
c1
c2
d3 #---第三列,在兩個檔案中都出現的行
d4