嘛,作為習慣使用Mac作為開發(fā)系統(tǒng)的開發(fā)者,Shell算是必須學習的內(nèi)容之一。CI的腳本之類的也需要用Shell來寫,總之學習一下沒有壞處。本文主要以基礎(chǔ)的Bourne Shell為學習對象,記錄學習過程中的一些知識要點,和其他筆記一樣,基本上是寫給自己看的,不喜歡的不要拍磚。
1.關(guān)于 #!
'#'表示注釋,而'#!'則告訴系統(tǒng)當前文件應(yīng)該用什么來執(zhí)行。
例如:
#!/bin/sh
echo Hello World
/bin/sh是Bourne shell在Unix下的標準路徑,如果是Linux下,它則是一個指向bash(最新版是dash)的軟連接。
2.關(guān)于chmod
對用戶創(chuàng)建的文件來說,默認只有讀寫權(quán)限,沒有執(zhí)行權(quán)限。為了讓腳本可以被執(zhí)行,就需要通過chmod命令修改權(quán)限。
r 表示讀權(quán)限 用數(shù)字表示為4
w 表示寫權(quán)限 用數(shù)字表示為2
x 表示執(zhí)行權(quán)限 用數(shù)字表示為1
- 表示無權(quán)限 用數(shù)字表示為0
chmod 777 test.sh
3.變量賦值
變量在賦值的時候沒有空格,否則系統(tǒng)會以為這是一條執(zhí)行命令語句,例如:
#!/bin/sh
MY_MESSAGE="Hello World"
echo $MY_MESSAGE
如果=左右有空格的話,系統(tǒng)還以為MY_MESSAGE是一個命令,而=和“Hello world”是命令的兩個參數(shù)。
如果變量沒有被賦值,那么變量將被視為一個空字符串。
4.使用read命令
read命令可以從標準輸入中讀取一行并將其賦值給你提供的變量。
#!/bin/sh
echo "What is your name?"
read MY_NAME
echo "Your name is $MY_NAME"
注意到read命令能夠自動給你的輸入加一對雙引號,這意味著你可以安全的使用空格。
What is your name?
Grand Kindom
Your name is Grand Kindom
5.變量的作用域
一般來說,變量的作用域只限于當前的shell環(huán)境,也就是#!bin/sh之下的環(huán)境中。
例如,我們有如下的shell腳本:
#!/bin/sh
echo "MY_VAR is $MY_VAR"
MY_VAR="Hello"
echo "MY_VAR is $MY_VAR"
現(xiàn)在我們執(zhí)行腳本:
$ MY_VAR=hello
$ ./test.sh
MY_VAR is
MY_VAR is Hello
可以發(fā)現(xiàn),我們在交互式shell(interactive shell)下賦值的變量,對新生成的用來運行腳本的子shell環(huán)境沒有任何影響。
為了能讓子shell可以使用我們的變量,需要使用export命令允許子shell繼承變量。
$ MY_VAR=World
$ export MY_VAR
$ ./test.sh
MY_VAR is World
MY_VAR is Hello
腳本中的第三行,我們更改了MY_VAR變量的值,我們嘗試在交互式shell下輸出:
$ echo $MY_VAR
World
發(fā)現(xiàn)值并沒有發(fā)生任何變化,也就是說子shell對父shell的變量沒有影響。
為了做到這點,我們需要將腳本在當前的交互式shell中運行,而不是新生成一個shell來運行。這一過程叫做source。通過.命令,我們可以source一個腳本:
$ . ./test.sh
MY_VAR is World
MY_VAR is Hello
$ echo $MY_VAR
Hello
MY_VAR變量被成功改變了!這也是.profile和.bashrc的工作原理。
6.變量的邊界
考慮以下腳本
#!/bin/sh
echo "Please tell your name"
read MY_NAME
echo "Hello $MY_NAME"
echo "Now I will create a file called $MY_NAME_file"
touch "$MY_NAME_file"
文件能夠創(chuàng)建成功嗎?不行。因為shell不知道變量的起始位置,它還以為有一個變量的名字叫做'MY_NAME_file',而事實上并沒有這樣的一個變量,因此文件創(chuàng)建失敗了。
使用花括號可以界定變量的邊界,他告訴shell:花括號內(nèi)的是一個變量,需要單獨對待。
echo "Please tell your name"
read MY_NAME
echo "Hello $MY_NAME"
echo "Now I will create a file called ${MY_NAME}_file"
touch "${MY_NAME}_file"
再次運行上述修改后的腳本,我們會發(fā)現(xiàn)文件被成功創(chuàng)建。
為什么touch命令的參數(shù)要用雙引號圈起來呢?這是為了防止你輸入的名字有空格。比如說你輸入的名字是Grand Kindom,那么最后的touch命令就會變成touch Grand Kingdom_file。結(jié)果會生成兩個文件,一個叫做Grand,另一個叫做Kindom_file。
7.循環(huán)
1)for循環(huán)
for i in hello 1 * 2 world
do
echo "i is set to $i in the loop now"
done
2)while循環(huán)
INPUT_STRING=hello
while [ "$INPUT_STRING" != "bye" ]
do
echo "Please type something in (bye to quit)"
read INPUT_STRING
echo "You typed: $INPUT_STRING"
done
當while后跟著一個:時,條件表達式永遠為true,這在某些情況下很有用。
while :
do
echo "Please type something int (^C to quit)"
read INPUT_STRING
echo "You typed: $INPUT_STRING"
done
另一個比較常見的使用方式是while read f循環(huán)
while read f
do
case $f in
hello) echo English ;;
howdy) echo American ;;
gday) echo Australian ;;
bonjour) echo French ;;
"guten tag") echo German ;;
*) echo Unknown Language: $f
;;
esac
done < myfile
8.測試
在shell中,[代表測試,它本身是一個程序,就像ls、chmod一樣,而不是一個符號。所以[后面必須跟空格,否則shell會把[和后面的字符拼在一起解釋,導致問題。在Unix系統(tǒng)中,[是一個指向test程序的軟連接,也就是說:
if [$foo = "bar" ]
會被解釋為if test$foo = "bar" ],這也就是問題的所在了。可以看到,在shell中,使用=比較字符串,而對于整數(shù)則使用-eq。
1) if
if [ ... ]
then
...
else
...
fi
注意then必須另起一行。也可以加上;使其和if在同一行。在shell中;表示后面的語句是另一行語句,不過出于某種目的(比如說節(jié)約空間)所以寫在同一行。與其對應(yīng)的符號是\,表示下一行的語句應(yīng)該和本行語句視為同一行。下面的第二個shell運用了邏輯運算符的短路性質(zhì)。
if [ ... ]; then
...
fi
[ "$X" -nt "/etc/passwd" ] && \
echo "X is a file which is newer than /etc/passwd"
也可以使用elif
if [ something ]; then
echo "Something"
elif [ something_else ]; then
echo "Something else"
else
echo "None of the above"
fi
9.預(yù)設(shè)變量
1) $0 $1 ... $9 和 $#
-
$0表示當前程序(腳本)的名稱 -
$1 .. $9表示調(diào)用腳本時候傳入的前9個參數(shù) - 變量
$@表示所有參數(shù)$*也有類似的作用但是它不保留空格和引號(通常情況下避免使用$*) -
$#表示腳本調(diào)用時候傳入的參數(shù)個數(shù)
#!/bin/sh
echo "I was called with $# parameters"
echo "My name is $0"
echo "My first parameter is $1"
echo "My second parameter is $2"
echo "All parameters are $@"
$ ./var3.sh hello world earth
I was called with 3 parameters
My name is ./var3.sh
My first parameter is hello
My second parameter is world
All parameters are hello world earth
2) $?
這個變量包含上一個命令的退出值。在shell中,返回值0表示正常退出,所以可以通過test來判斷上一個命令執(zhí)行的時候有沒有執(zhí)行成功,增加程序的魯棒性。
#!/bin/sh
/usr/local/bin/my-command
if [ "$?" -ne "0" ]; then
echo "Sorry, we had a problem there!"
fi
3)$$和$!
-
$$變量表示當前的進程標識符,如果你的腳本允許存在多個實例同時運行的話,在創(chuàng)建文件的時候就可以在文件名上帶上這個進程標識符避免沖突:/temp/my_file.$$。 -
$!變量表示最后運行的后臺進程標識符
10.花括號的高級用法
花括號除了用來保證變量的邊界外,還可以用來處理字符串undefined或者為null的情況(這兩者在shell里基本沒有區(qū)別),為其添加一個默認值,方法是在變量名后面加上:-
#!/bin/sh
echo "What is your name [ `whoami` ] \c"
read MY_NAME
echo "Hello ${MY_NAME:-`whoami`}"
上述腳本中的\c告訴echo命令不要換行。反引號中的內(nèi)容whoami被視為一個命令,命令在執(zhí)行之后的標準輸出會被替換為反引號所在的內(nèi)容。而whoami命令輸出的是當前登錄的用戶名稱,所以上述命令的作用是讓用戶鍵入自己的名稱,并且會顯示當前登錄的用戶名稱作為默認值,如果用戶直接點了回車沒有輸入內(nèi)容的話,則會輸出用戶的登錄名。
$ ./test.sh
What is your name [ melot ] abc
Hello abc