參考:《Shell從入門到精通》
參考:Shell 編程快速入門
參考:Shell 教程
一、Shell
Shell腳本(英語:Shell script)是一種電腦程序與文本文件,內容由一連串的shell命令組成,由Unix shell扮演命令行解釋器的角色,經由Unix Shell直譯其內容後運作。
Shell的用處:
- shell簡單、靈活、高效,特別適合處理一些系統管理方面的小問題
- shell可以實現自動化管理,讓系統管理員的工作變得容易、簡單、高效
- shell腳本可移植性好,在unix/linux系統中可靈活移植,幾乎不用任何設置就能正常運行
- shell腳本可輕鬆方便讀取和修改源代碼,不需要編譯
- 資源密集型的任務,尤其在需要考慮效率時(比如排序,hash 等)
- 需要處理大任務的數學操作,尤其是浮點運算,精確運算,或者複雜的算術運算(這種情況一般使用C++或FORTRAN 來處理)
- 有跨平臺移植需求(一般使用C 或Java)
- 複雜的應用,在必須使用結構化編程的時候(需要變量的類型檢查,函數原型,等等)
- 對於影響系統全局性的關鍵任務應用。
- 對於安全有很高要求的任務,比如你需要一個健壯的系統來防止入侵,破解,惡意破壞等等.
- 需要直接操作系統硬件、需要產生或操作圖形化界面 GUI
- 私人的,閉源的應用(shell 腳本把代碼就放在文本文件中,全世界都能看到)
二、Shell環境
Linux
Linux默認安裝就帶了shell解釋器。
Mac OS
Mac OS不僅帶了sh、bash這兩個最基礎的解釋器,還內置了ksh、csh、zsh等不常用的解釋器。
Windows上的模擬器
windows出廠時沒有內置shell解釋器,需要自行安裝,爲了同時能用grep, awk, curl等工具,最好裝一個cygwin或者mingw來模擬linux環境。
即Bourne shell,POSIX(Portable Operating System Interface)標準的shell解釋器,它的二進制文件路徑通常是/bin/sh,由Bell Labs開發。
bash
Bash是Bourne shell的替代品,屬GNU Project,二進制文件路徑通常是/bin/bash。在CentOS裏,/bin/sh是一個指向/bin/bash的符號鏈接,但在Mac OS上不是,/bin/sh和/bin/bash是兩個不同的文件,儘管它們的大小隻相差100字節左右,業界通常混用bash、sh、和shell。
三、Shell的解釋和執行
- #!/bin/sh
- #!/bin/bash
- #!/bin/csh
- #!/bin/tcsh
- #!/bin/ksh
//爲shell腳本直接加上可執行權限並執行
chmod 755 run.sh
./run.sh
//通過sh命令執行shell腳本
sh run.sh
四、Shell中的特殊字符
1. 美元符號
美元符號表示變量替換,即用其後面指定的變量的值來代替變量。反斜線“\”爲轉義字符,轉義字符告訴shell不要對其後面的那個字符進行特殊處理,只是當做普通字符。而shell下的引號情況比較複雜,分爲三種:雙引號("),單引號(')和倒引號(`)。他們的作用都不盡相同,以下一一說明。
2. 雙引號(")
由雙引號括起來的字符,除$,倒引號(`)和反斜線(\)仍保留其特殊功能外,其餘字符均作爲普通字符對待。
3. 單引號(')
由單引號括起來的字符都作爲普通字符出現。
4. 倒引號(`)
由倒引號括起來的字符串被shell解釋爲命令行,在執行時,shell會先執行該命令,並以它的標準輸出結果取代整個引號部分。
示例1的代碼及輸出如下:
#echo "My current directory is `pwd` and logname is $LOGNAME"
My current directory is /root and logname is root
示例2的代碼及輸出如下:#echo "My current directory is `pwd` and logname is \$LOGNAME"
My current directory is /root and logname is $LOGNAME
示例3的代碼及輸出如下:#echo 'My current directory is `pwd` and logname is $LOGNAME'
【單引號裏面的內容不變】My current directory is `pwd`and logname is $LOGNAME
五、Shell中的變量
variable=value(注:等號左右不能有空格)
2. 可以在同一行中對多個變量賦值a=5 b="a string"
3. 使用變量$variable 或 ${variable}
4. 只讀變量myUrl="http://www.w3cschool.cc"
readonly myUrl
myUrl="http://www.baidu.com"
運行腳本,結果如下:/bin/sh: NAME: This variable is read only.
5. 刪除變量unset variable_name
變量被刪除後不能再次使用。unset 命令不能刪除只讀變量。1) 局部變量 局部變量在腳本或命令中定義,僅在當前shell實例中有效,其他shell啓動的程序不能訪問局部變量。
2) 環境變量 所有的程序,包括shell啓動的程序,都能訪問環境變量,有些程序需要環境變量來保證其正常運行。必要的時候shell腳本也可以定義環境變量。
3) shell變量 shell變量是由shell程序設置的特殊變量。shell變量中有一部分是環境變量,有一部分是局部變量,這些變量保證了shell的正常運行
7. 變量的通配符
假設有變量 var=http://www.aaa.com/123.htm
1. # 號截取,從左邊開始刪除第一個匹配到的字符及左邊的字符,保留右邊字符。
echo ${var#*//}
# 號是運算符,*// 表示 // 號及左邊的所有字符,即刪除 http://
結果是 :www.aaa.com/123.htm
2. ## 號截取,從左邊開始刪除最後一個匹配到的字符及左邊的字符,保留右邊字符。
echo ${var##*/}
##*/ 表示從左邊開始刪除最後(最右邊)一個 / 號及左邊的所有字符,即刪除 http://www.aaa.com/
結果是 123.htm
3. %號截取,刪除從右開始第一個匹配到的字符及右邊字符,保留左邊字符
echo ${var%/*}
%/* 表示從右邊開始,刪除第一個 / 號及右邊的字符
結果是:http://www.aaa.com
4. %% 號截取,刪除從右開始最後一個匹配到的字符及右邊字符,保留左邊字符
echo ${var%%/*}
%%/* 表示從右邊開始,刪除最後(最左邊)一個 / 號及右邊的字符
結果是:http:
5. :從左邊第幾個字符開始,及字符的個數
echo ${var:0:5}
其中的 0 表示左邊第一個字符開始,5 表示字符的總個數。
結果是:http:
6. 從左邊第幾個字符開始,一直到結束。
echo ${var:7}
其中的 7 表示左邊第8個字符開始,一直到結束。
結果是 :www.aaa.com/123.htm
7. 從右邊第幾個字符開始,及字符的個數
echo ${var:0-7:3}
其中的 0-7 表示右邊算起第七個字符開始,3 表示字符的個數。
結果是:123
8. 從右邊第幾個字符開始,一直到結束。
echo ${var:0-7}
表示從右邊第七個字符開始,一直到結束。
結果是:123.htm
注:(左邊的第一個字符是用 0 表示,右邊的第一個字符用 0-1 表示)
六、Shell中的數組
array_name=(value0 value1 value2 value3)
用括號來表示數組,數組元素用"空格"符號分割開。還可以單獨定義數組的各個分量:array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen
2. 讀取數組valuen=${array_name[n]}
使用@符號可以獲取數組中的所有元素3. 獲取數組的長度
# 取得數組元素的個數
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得數組單個元素的長度
lengthn=${#array_name[n]}
七、Shell中的函數和運算符
7.1 函數語法形式
function name {
commands
return
}
或name () {
commands
return
}
參數處理 | 說明 |
---|---|
$# | 傳遞到腳本或函數的參數個數 |
$* |
以一個單字符串顯示所有向腳本傳遞的參數。 如"$*"用「"」括起來的情況、以"$1 $2 … $n"的形式輸出所有參數。 |
$$ | 腳本運行的當前進程ID號 |
$! | 後臺運行的最後一個進程的ID號 |
$@ |
與$*相同,但是使用時加引號,並在引號中返回每個參數。 如"$@"用「"」括起來的情況、以"$1" "$2" … "$n" 的形式輸出所有參數。 |
$- | 顯示Shell使用的當前選項,與set命令功能相同。 |
$? | 顯示最後命令的退出狀態。0表示沒有錯誤,其他任何值表明有錯誤。 |
7.2 表達式計算工具
#!/bin/bash
val=`expr 2 + 2`
echo "兩數之和爲 : $val"
表達式和運算符之間要有空格,例如 2+2 是不對的,必須寫成 2 + 2,這與我們熟悉的大多數編程語言不一樣。完整的表達式要被 ` ` 包含,注意這個字符不是常用的單引號,在 Esc 鍵下邊。7.3 算數運算符
運算符 | 說明 | 舉例 |
---|---|---|
+ | 加法 | `expr $a + $b` 結果爲 30。 |
- | 減法 | `expr $a - $b` 結果爲 -10。 |
* | 乘法 | `expr $a \* $b` 結果爲 200。 |
/ | 除法 | `expr $b / $a` 結果爲 2。 |
% | 取餘 | `expr $b % $a` 結果爲 0。 |
= | 賦值 | a=$b 將把變量 b 的值賦給 a。 |
== | 相等。用於比較兩個數字,相同則返回 true。 | [ $a == $b ] 返回 false。 |
!= | 不相等。用於比較兩個數字,不相同則返回 true。 | [ $a != $b ] 返回 true。 |
7.4 關係運算符
運算符 | 說明 | 舉例 |
---|---|---|
-eq | 檢測兩個數是否相等,相等返回 true。 | [ $a -eq $b ] 返回 false。 |
-ne | 檢測兩個數是否相等,不相等返回 true。 | [ $a -ne $b ] 返回 true。 |
-gt | 檢測左邊的數是否大於右邊的,如果是,則返回 true。 | [ $a -gt $b ] 返回 false。 |
-lt | 檢測左邊的數是否小於右邊的,如果是,則返回 true。 | [ $a -lt $b ] 返回 true。 |
-ge | 檢測左邊的數是否大於等於右邊的,如果是,則返回 true。 | [ $a -ge $b ] 返回 false。 |
-le | 檢測左邊的數是否小於等於右邊的,如果是,則返回 true。 | [ $a -le $b ] 返回 true。 |
7.5 布爾運算符
運算符 | 說明 | 舉例 |
---|---|---|
! | 非運算,表達式爲 true 則返回 false,否則返回 true。 | [ ! false ] 返回 true。 |
-o | 或運算,有一個表達式爲 true 則返回 true。 | [ $a -lt 20 -o $b -gt 100 ] 返回 true。 |
-a | 與運算,兩個表達式都爲 true 才返回 true。 | [ $a -lt 20 -a $b -gt 100 ] 返回 false。 |
7.6 邏輯運算符
運算符 | 說明 | 舉例 |
---|---|---|
&& | 邏輯的 AND | [[ $a -lt 100 && $b -gt 100 ]] 返回 false |
|| | 邏輯的 OR | [[ $a -lt 100 || $b -gt 100 ]] 返回 true |
7.7 字符串運算符
運算符 | 說明 | 舉例 |
---|---|---|
= | 檢測兩個字符串是否相等,相等返回 true。 | [ $a = $b ] 返回 false。 |
!= | 檢測兩個字符串是否相等,不相等返回 true。 | [ $a != $b ] 返回 true。 |
-z | 檢測字符串長度是否爲0,爲0返回 true。 | [ -z $a ] 返回 false。 |
-n | 檢測字符串長度是否爲0,不爲0返回 true。 | [ -n $a ] 返回 true。 |
str | 檢測字符串是否爲空,不爲空返回 true。 | [ $a ] 返回 true。 |
7.8 文件測試運算符
操作符 | 說明 | 舉例 |
---|---|---|
-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。 |
八、Shell中的邏輯控制語句
1. if 語句
if commands; then
commands
elif commands; then
commands
else
commands
fi
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
當變量值在列表裏,for循環即執行一次所有命令,使用變量名獲取列表中的當前取值。命令可爲任何有效的shell命令和語句。in列表可以包含替換、字符串和文件名。in列表是可選的,如果不用它,for循環使用命令行的位置參數。while condition
do
command
done
until condition
do
command
done
條件可爲任意測試條件,測試發生在循環末尾,因此循環至少執行一次—請注意這一點。case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac
case工作方式如上所示。取值後面必須爲單詞in,每一模式必須以右括號結束。取值可以爲變量或常數。匹配發現取值符合某一模式後,其間所有命令開始執行直至 ;;。九、Shell中的測試語句
或
[ expression ]
正則表達式和[[ expression ]]
#!/bin/bash
# test-integer2: evaluate the value of an integer.
INT=-5
if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
if [ $INT -eq 0 ]; then
echo "INT is zero."
else
if [ $INT -lt 0 ]; then
echo "INT is negative."
else
echo "INT is positive."
fi
if [ $((INT % 2)) -eq 0 ]; then
echo "INT is even."
else
echo "INT is odd."
fi
fi
else
echo "INT is not an integer." >&2
exit 1
fi
整數複合命令(( ))
#!/bin/bash
# test-integer2a: evaluate the value of an integer.
INT=-5
if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
if ((INT == 0)); then
echo "INT is zero."
else
if ((INT < 0)); then
echo "INT is negative."
else
echo "INT is positive."
fi
if (( ((INT % 2)) == 0)); then
echo "INT is even."
else
echo "INT is odd."
fi
fi
else
echo "INT is not an integer." >&2
exit 1
fi
十、Shell中的輸入和交互
echo -n "Please enter an integer -> "
read var
echo "var = '$var'"
給多個變量賦值:echo -n "Enter one or more values > "
read var1 var2
echo "var1 = '$var1'"
echo "var2 = '$var2'"
若沒有提供變量名,shell 變量 REPLY 會包含數據行echo -n "Enter one or more values > "
read
echo "REPLY = '$REPLY'"
read選項:read [-options] [variable...]- -a array:把輸入賦值到數組 array 中,從索引號零開始
- -d delimiter:用字符串 delimiter 中的第一個字符指示輸入結束,而不是一個換行符
- -e:使用 Readline 來處理輸入。這使得與命令行相同的方式編輯輸入
- -n num:讀取 num 個輸入字符,而不是整行
- -p prompt:爲輸入顯示提示信息,使用字符串 prompt
- -r:Raw mode. 不把反斜槓字符解釋爲轉義字符
- -s:Silent mode. 不會在屏幕上顯示輸入的字符。當輸入密碼和其它確認信息的時候,這會很有幫助
- -t seconds:超時. 幾秒鐘後終止輸入。read 會返回一個非零退出狀態,若輸入超時
- -u fd:使用文件描述符 fd 中的輸入,而不是標準輸入
#!/bin/bash
if read -t 10 -sp "Enter secret pass phrase > " secret_pass; then
echo -e "\nSecret pass phrase = '$secret_pass'"
else
echo -e "\nInput timed out" >&2
exit 1
fi
輸入校正#!/bin/bash
invalid_input () {
echo "Invalid input '$REPLY'" >&2
exit 1
}
read -p "Enter a single item > "
# input is empty (invalid)
[[ -z $REPLY ]] && invalid_input
# input is multiple items (invalid)
(( $(echo $REPLY | wc -w) > 1 )) && invalid_input
# is input a valid filename?
if [[ $REPLY =~ ^[-[:alnum:]\._]+$ ]]; then
echo "'$REPLY' is a valid filename."
if [[ -e $REPLY ]]; then
echo "And file '$REPLY' exists."
else
echo "However, file '$REPLY' does not exist."
fi
# is input a floating point number?
if [[ $REPLY =~ ^-?[[:digit:]]*\.[[:digit:]]+$ ]]; then
echo "'$REPLY' is a floating point number."
else
echo "'$REPLY' is not a floating point number."
fi
# is input an integer?
if [[ $REPLY =~ ^-?[[:digit:]]+$ ]]; then
echo "'$REPLY' is an integer."
else
echo "'$REPLY' is not an integer."
fi
else
echo "The string '$REPLY' is not a valid filename."
fi
菜單驅動#!/bin/bash
# read-menu: a menu driven system information program
clear
echo "
Please Select:
1. Display System Information
2. Display Disk Space
3. Display Home Space Utilization
0. Quit
"
read -p "Enter selection [0-3] > "
if [[ $REPLY =~ ^[0-3]$ ]]; then
if [[ $REPLY == 0 ]]; then
echo "Program terminated."
exit
fi
if [[ $REPLY == 1 ]]; then
echo "Hostname: $HOSTNAME"
uptime
exit
fi
if [[ $REPLY == 2 ]]; then
df -h
exit
fi
if [[ $REPLY == 3 ]]; then
if [[ $(id -u) -eq 0 ]]; then
echo "Home Space Utilization (All Users)"
du -sh /home/*
else
echo "Home Space Utilization ($USER)"
du -sh $HOME
fi
exit
fi
else
echo "Invalid entry." >&2
exit 1
fi
十一、文件重定向
1. linux文件描述符
- 標準輸入 standard input 0
- 正確輸出 standard output 1
- 錯誤輸出 error output 2
2. 輸出重定向
command-line1 [1-n] > file或文件操作符或設備
將一條命令執行結果(標準輸出,或者錯誤輸出,本來都要打印到屏幕上面的) 重定向其它輸出設備(文件,打開文件操作符,或打印機等等)1,2分別是標準輸出,錯誤輸出。#把錯誤輸出,不輸出到屏幕,輸出到err.txt
[zenhobby@zencode shell]$ ls test.sh test1.sh 1>suc.txt 2>err.txt
[zenhhobby@zencode shell]$ cat suc.txt err.txt
test.sh
ls: test1.sh: 沒有這個文件和目錄
#繼續追加把輸出寫入suc.txt err.txt “>>”追加操作符
[zenhobby@zencode shell]$ ls test.sh test1.sh 1>>suc.txt 2>>err.txt
命令 | 說明 |
---|---|
command > file | 將輸出重定向到 file。 |
command < file | 將輸入重定向到 file。 |
command >> file | 將輸出以追加的方式重定向到 file。 |
n > file | 將文件描述符爲 n 的文件重定向到 file。 |
n >> file | 將文件描述符爲 n 的文件以追加的方式重定向到 file。 |
n >& m | 將輸出文件 m 和 n 合併。 |
n <& m | 將輸入文件 m 和 n 合併。 |
<< tag | 將開始標記 tag 和結束標記 tag 之間的內容作爲輸入。 |
1、shell遇到”>”操作符,會判斷右邊文件是否存在,如果存在就先刪除,並且創建新文件。不存在直接創建。 無論左邊命令執行是否成功。右邊文件都會變爲空。
2、“>>”操作符,判斷右邊文件,如果不存在,先創建。以添加方式打開文件,會分配一個文件描述符[不特別指定,默認爲1,2]然後,與左邊的標準輸出(1)或錯誤輸出(2) 綁定。
3、當命令:執行完,綁定文件的描述符也自動失效。0,1,2又會空閒。
4、一條命令啓動,命令的輸入,正確輸出,錯誤輸出,默認分別綁定0,1,2文件描述符。
5、一條命令在執行前,先會檢查輸出是否正確,如果輸出設備錯誤,將不會進行命令執行
3. 輸入重定向
command-line [n] <file或文件描述符&設備
命令將默認從鍵盤獲得的輸入,改成從文件,或者其它打開文件以及設備輸入。執行這個命令,將標準輸入0,與文件或設備綁定。將由它進行輸入。[zenhobby@zencode shell]# cat > catfile
testing
cat file test
#這裏按下 [ctrl]+d 離開
#從標準輸入【鍵盤】獲得數據,然後輸出給catfile文件
[zenhobby@zencode shell]$ cat>catfile <test.sh
#cat 從test.sh 獲得輸入數據,然後輸出給文件catfile
[zenhobby@zencode shell]$ cat>catfile <<eof
test a file
test!
eof
#<< 這個連續兩個小符號, 他代表的是『結束的輸入字符』的意思。這樣當空行輸入eof字符,輸入自動結束,不用ctrl+D
4. 重定向到空設備文件
1>/dev/null 2>&1
1. 標準輸入stdin文件描述符爲0,標準輸出stdout文件描述符爲1,標準錯誤stderr文件描述符爲22. /dev/null 空設備文件,相當於垃圾桶
- 0代表標準輸入
- 1代表stdout標準輸出,默認值爲1,所以”1>/dev/null”可以簡寫爲”>/dev/null”
- 2代表stderr標準錯誤輸出
所以,1>/dev/null 2>&1的解釋就是:
將stdout標準輸出重定向到空設備文件/dev/null ,同時將stderr標準錯誤輸出的重定向跟stdout標準輸出重定向一致,也輸出到空設備文件/dev/null。