Linux有多神奇,我就不說了。作為在一個IT界混的碼農,或多或少的都會接觸Linux,不管你是怎麼接觸Linux,對於其中的一些命令肯定也會或多或少的了解一些。Linux雖然不是你的必須裝備,但是裝備上了Linux,對於你的職業生涯肯定是有百利而無一害的。基於此,我接觸了Linux,然後就學習了一下Shell腳本,以此文章,作為Shell腳本學習中的一個筆記,以備後期翻閱。
學習一項知識,憑的是興趣,這是一個理由;另一個理由則是為了工作。好吧,我高尚一回,我覺對不是為了生計而去學習Shell腳本,純粹是為了興趣,彌補我大學時期沒有學習Shell腳本而留下來的遺憾。而更多的人學習Shell並不是和我一樣,更多的是為了工作,解決工作中的實際問題而決定要看看Shell腳本的,那麼Shell腳本在工作中到底能干什麼呢?先看看以下問題:
如果對於上面的問題,如果你回答“YES”。那你就非常有必要去學習一下Linux Shell腳本。好了,想必你已經決定了去看看Shell了。來吧。
首先來一段簡單的Shell腳本,整體上對Shell有一個簡單的認識。
#!/bin/bash
cd ~ # 切換到用戶的主目錄,也就是/home/xxx
mkdir shell_test # 創建一個名為shell_test的文件夾for ((i=0; i<10; i++)) # Shell的for循環do
touch test_$i.txt #創建空文件,文件名為test_0.txt, test_1.txt ...done
上面是一段簡單的Shell程序,實現在功能為:
由於是第一個Shell程序,所以有必要逐行解釋一下,留下一個好的印象。
cd
、mkdir
和touch
都是系統自帶的程序,一般在/bin
目錄下;而for
、do
和done
都是Shell的關鍵字。 Shell中使用#
開頭的行就是注釋。
一個簡單的程序之後,我們就開始進入Shell的語法學習吧。
從變量開始學習這段旅程。
基礎知識
1. 在Shell中,使用變量之前不需要事先聲明,只是通過使用它們來創建它們;
2. 在默認情況下,所有變量都被看做是字符串,並以字符串來存儲;
3. Shell變量是區分大小寫的;
4. 在Shell中,通過在變量名前加一個$
符號來訪問它的內容,無論何時想要獲取變量內容,都必須在它前面加一個$
符號;
下面通過一段Shell腳本來詳細的說明上面的內容:
#!/bin/bash
var1=10 #定義一個變量var1#定義一個變量var2,對於字符串中有空格的情況,需要使用引號將字符串括起來,#此外等號兩邊不能有空格
var2="Jelly Think"
echo $var1 #獲取變量var1的值
echo $var2 #獲取變量var2的值
使用read
在Shell中,我們可以使用read
命令將用戶的輸入賦值給一個變量。這個命令需要一個參數,即准備讀入用戶輸入數據的變量名,然後它會等待用戶輸入數據。通常情況下,在用戶按下回車鍵時,read命令結束。例如以下代碼:
#!/bin/bash
read var1 #讀入用戶輸入的var1變量的值
echo $var1 #輸出var1的值exit 0
引號的使用技巧
在上面的代碼中也說了,如果字符串中包含了空格,就需要使用引號將字符串括起來,而這只是引號的一個簡單的使用。 像$var1這種的變量在引號中的行為取決於你所使用的引號類型。這句話有以下兩層意思:
但是,我們可以通過在$字符前面加上一個\
字符取消它的特殊含義,也就是經常說的轉義字符。下面通過一段簡單的代碼來理解上面內容的意思:
#!/bin/bash
var1="JellyThink"
echo "This is $var1" #輸出:This is JellyThink
echo 'This is $var1' #輸出:This is $var1
echo "This is \$var1" #輸出:This is $var1exit 0
環境變量
當一個Shell腳本程序開始執行時,一些變量根據環境設置中的值進行初始化,一般比較常用的有以下幾個:
/home/jelly
;參數變量
如果腳本程序在調用時帶有參數,一些額外的變量就會被創建。即使沒有傳遞任何參數,環境變量$#也依然存在,只不過它的值是0罷了。例如以下實力代碼:
#!/bin/bash#參數的命名是$1、$2......#$0表示腳本的名字#輸出參數個數
echo "參數個數為:$#"
echo "腳本名字:$0"
echo "第一個參數:$1"
echo "第二個參數:$2"exit 0
輸出如下:
參數個數為:2腳本名字:./argsDemo.sh
第一個參數:Jelly第二個參數:Think
我們知道,所有程序設計語言的基礎是對條件進行測試判斷,並根據測試結果采取不同行動的能力。在總結之前,先看看Shell腳本程序裡可以使用的條件結構,然後再來看看使用這些條件的控制結構。
一個Shell腳本能夠對任何可以從命令行上調用的命令的退出碼進行判斷,這其中也包括我們寫的Shell腳本程序。這就是為什麼要在所有自己編寫的腳本程序的結尾包括一條返回值的exit
命令的重要原因。
test或[命令
在實際工作中,大多數腳本程序都會廣泛使用Shell的布爾判斷命令[
或test
。在一些系統上,這兩個命令其實是一樣的,只是為了增強可讀性,當使用[
命令時,我們還使用符號]
來結尾。寫了這麼多程序了,還是頭一次見,將一個[
作為一個命令。算我孤陋寡聞了。下面就通過一個簡單的例子來看看test
和[
是如何使用的。
#!/bin/bashif test -f testDemo.sh
then
echo "testDemo.sh exists."fiif [ -f test.c ]then
echo "test.c exists."else
echo "I cannot find exists."fiexit 0
我們通過使用-f
來判斷指定文件是否存在。test
命令可以使用的條件類型可以歸為3類:
下面就分別看看。
字符串比較
結果
string1 = string2
如果兩個字符串相同,結果就為真
string1 != string2
如果兩個字符串不同,結果就為真
-n string
如果字符串不為空,則結果為真
-z string
如果字符串為一個空串(null),則結果為真
算術比較
結果
expression1 -eq expression2
如果兩個表達式相等,則結果為真
expression1 -ne expression2
如果兩個表達式不等,則結果為真
expression1 -gt expression2
如果expression1大於expression2,則為真
expression1 -ge expression2
如果expression1大於等於expression2,則為真
expression1 -lt expression2
如果expression1小於expression2,則為真
expression1 -le expression2
如果expression1小於等於expression2,則為真
!expression
表達式為假,則結果就為真;反之亦然
文件條件測試
結果
-d file
如果文件是一個目錄,則為真
-f file
如果文件是一個普通文件,則為真;也可以用來測試文件是否存在
-r file
如果文件可讀,則結果為真
-s file
如果文件大小不為0,則結果為真
-w file
如果文件可寫,則結果為真
-x file
如果文件可執行,則結果為真
Shell也有一組控制結構,它們與其它程序語言中的控制接口很相似,下面就分開來總結它們。
if語句
結構如下:
if condition
then
statements
else
statements
fi
代碼示例:
#!/bin/bash
var1=Jelly #這個是賦值,不能有空格分隔
var2=Thinkif [ $var1 = $var2 ] #‘=’號兩邊都需要空格分隔then
echo "$var1 = $var2"else
echo "$var1 != $var2"fiexit 0
elif語句
結構如下
if condition
then
statements
elif condition
then
statements
else
statements
fi
一個很容易出錯的問題。例如以下代碼:
#!/bin/bash
echo "Is it morning? please answer yes or no."
read timeofday
if [ $timeofday = "yes" ]then
echo "Good morning."elif [ $timeofday = "no" ]then
echo "Good afternoon."else
echo "Sorry, $timeofday not recognized. Enter yes or no."
exit 1fiexit 0
上面這段代碼很簡單,運行以下,如果你不輸入yes
或no
,就會運行出錯,得到以下提示信息:
./elifDemo1.sh: line 6: [: =: unary operator expected
./elifDemo1.sh: line 9: [: =: unary operator expected
這是為何?代碼中有if [ $timeofday = "yes" ]
,當我不輸入任何內容時,這個if
語句就會變成這樣if [ = "yes" ]
,很明顯,這不是一個合法的條件。為了避免出現這種情況,我們必須給變量加上引號,改成這樣if [ "$timeofday" = "yes" ]
。這樣就沒有問題了。
for語句
結構如下:
for variable in values
do
statements
done
代碼示例:
#!/bin/bashfor foo in Jelly Think Websitedo
echo $foo
doneexit 0
在文章的開頭,那段簡單的代碼中,也包含了for
的簡單使用。
while語句
結構如下:
while condition
do
statements
done
代碼示例:
#!/bin/bash
echo "Enter password: "
read pwd
while [ "$pwd" != "root" ]do
echo "Sorry, try again."
read pwd
doneexit 0
until語句
結構如下:
until condition
do
statements
done
它與while
循環很相似,只是把條件測試反過來了;也就是說,循環將反復執行直到條件為真。
case語句
結構如下:
case variable in
pattern [ | pattern] ...) statements;;
pattern [ | pattern] ...) statements;;
...esac
case
的代碼結構相對來說是比較復雜的。case
結構具備匹配多個模式,然後執行多條相關語句的能力,這使得它非常適合於處理用戶的輸入。下面通過一個實例來看看case
的具體使用。
#!/bin/bash
echo "Is it morning? Please answer yes or no."
read input
case "$input" in
yes ) echo "Good Morning.";;
no ) echo "Good Afternoon.";;
y ) echo "Good Morning.";;
n ) echo "Good Afternoon.";;
* ) echo "Sorry, answer not recognized";;esacexit 0
當case
語句被執行時,它會把變量input
的內容與各字符串依次進行比較。一旦某個字符串與輸入匹配成功,case
命令就會執行緊隨右括號)
後面的代碼,然後就結束。 在代碼中,最後面的*
表示匹配任何字符串,我們在寫代碼時,總是在其它匹配之後再添加一個*
以確保如果沒有字符串得到匹配,case
語句也會執行某個默認動作。 由於case
比較復雜,所以不得不再來一段代碼,究其用法,如下:
#!/bin/bash
echo "Is it morning? Please answer yes or no."
read input
case "$input" in
yes | y | Yes | YES ) echo "Good Morning.";;
n* | N* ) echo "Good Afternoon.";;
* ) echo "Sorry, answer not recognized.";;esacexit 0
上面這段代碼,使用了|
符號,也就是說,也就是合並了多個匹配模式;同時還使用了*
通配符;沒有問題,上面的代碼運行的很好。
&&和||操作符
Shell中也支持&&
和||
符號,和C++語言中的是一樣;比如:
statement1 && statement2 && statement3 && ...
從左開始順序執行每條命令,如果一條命令返回的是true
,它右邊的下一條命令才能執行。如果此持續直到有一條命令返回false
,或者列表中的所有命令都執行完畢;遵循“短路”規則。
statement1 || statement2 || statement3 || ...
從左開始順序執行每條命令,如果一條命令返回的是false
,它右邊的下一條命令才能夠被執行。如此持續直到有一條命令返回true
,或者列表中的所有命令都執行完畢。
語句塊
在Shell中也有語句塊,使用{}
來定義一個語句塊。我們可以把多條語句放到一個語句塊中執行。
函數
函數,這麼NB的東西,Shell怎麼能少呢。定義函數的結構如下:
function_name(){
statements
}
代碼示例:
#!/bin/bash
foo #運行到這裡,還沒有定義foo函數,會報錯
foo() {
echo "Function foo is executing."}
echo "script starting"
foo #輸出"Function foo is executing."
echo "script ended"exit 0
腳本程序從自己的頂部開始執行,當它遇到了foo() {
結構時,它知道腳本正在定義一個名為foo
的函數。當執行到單獨的foo
時,Shell就知道應該去執行剛才定義的函數了。函數執行完畢以後,腳本接著foo
後的代碼繼續執行。 當一個函數被調用時,腳本程序的位置參數($*
、$@
、$#
、$1
、$2
等)會被替換為函數的參數,這也是我們讀取傳遞給函數的參數的方法。當函數執行完畢後,這些參數會恢復為它們先前的值。 我們可以使用local
關鍵字在Shell函數中聲明局部變量,局部變量將僅在函數的作用范圍內有效。此外,函數可以訪問全局作用范圍內的其它Shell變量。如果一個局部變量和一個全局變量的名字相同,前者就會覆蓋後者,但僅限於函數的作用范圍之內,例如以下代碼:
#!/bin/bash
sample_text="global variable"
foo(){
local sample_text="local variable"
echo "Function foo is executing..."
echo $sample_text
}
echo "script starting..."
echo $sample_text
foo
echo "script ended..."
echo $sample_text
exit 0
輸出如下:
script starting...global variable
Function foo is executing...local variable
script ended...global variable
終於總結完第一部分了,還有第二部分,在下一篇博文中將主要總結Shell中經常使用的一些命令。這篇對Shell中的一些基礎進行總結的博文就到此了