標籤:http 學習 複雜度 重定義 class 表示 ble 指令碼 成本
什麼是Shell指令碼
Shell指令碼(英語:Shell script),又稱Shell命令稿、程式化指令碼,是一種電腦程式與文字檔,內容由一連串的shell命令組成,經由Unix Shell直譯其內容後運作。被當成是一種指令碼語言來設計,其運作方式與直譯語言相當,由Unix shell扮演命令列解譯器的角色,在讀取shell指令碼之後,依序運行其中的shell命令,之後輸出結果。利用shell指令碼可以進行系統管理,檔案操作等。
樣本
看個例子吧:
#!/bin/shcd ~mkdir shell_tutcd shell_tut for ((i=0; i<10; i++)); do touch test_$i.txtdone
樣本解釋
- ·第1行:指定指令碼解譯器,這裡是用/bin/sh做解譯器的
- ·第2行:切換到目前使用者的home目錄
- ·第3行:建立一個目錄shell_tut
- ·第4行:切換到shell_tut目錄
- ·第5行:迴圈條件,一共迴圈10次
- ·第6行:建立一個test_1…10.txt檔案
- ·第7行:迴圈體結束
cd, mkdir, touch都是系統內建的程式,一般在/bin或者/usr/bin目錄下。for, do, done是sh指令碼語言的關鍵字。
shell和shell指令碼的概念
shell是指一種應用程式,這個應用程式提供了一個介面,使用者通過這個介面訪問作業系統核心的服務。Ken Thompson的sh是第一種Unix Shell,Windows Explorer是一個典型的圖形介面Shell。
shell指令碼(shell script),是一種為shell編寫的指令碼程式。業界所說的shell通常都是指shell指令碼,但讀者朋友要知道,shell和shell script是兩個不同的概念。由於習慣的原因,簡潔起見,本文出現的“shell編程”都是指shell指令碼編程,不是指開發shell自身(如Windows Explorer擴充開發)。
環境
shell編程跟java、php編程一樣,只要有一個能編寫代碼的文字編輯器和一個能解釋執行的指令碼解譯器就可以了。
OS
當前主流的作業系統都支援shell編程,本文檔所述的shell編程是指Linux下的shell,講的基本都是POSIX標準下的功能,所以,也適用於Unix及BSD(如Mac OS)。
Linux
Linux預設安裝就帶了shell解譯器。
Mac OS
Mac OS不僅帶了sh、bash這兩個最基礎的解譯器,還內建了ksh、csh、zsh等不常用的解譯器。
Windows上的模擬器
windows出廠時沒有內建shell解譯器,需要自行安裝,為了同時能用grep, awk, curl等工具,最好裝一個cygwin或者mingw來類比linux環境。
指令碼解譯器sh
即Bourne shell,POSIX(Portable Operating System Interface)標準的shell解譯器,它的二進位檔案路徑通常是/bin/sh,由Bell Labs開發。
本文講的是sh,如果你使用其它語言用作shell編程,請自行參考相應語言的文檔。
bash
Bash是Bourne shell的替代品,屬GNU Project,二進位檔案路徑通常是/bin/bash。業界通常混用bash、sh、和shell,比如你會經常在招聘營運工程師的文案中見到:熟悉Linux Bash編程,精通Shell編程。
在CentOS裡,/bin/sh是一個指向/bin/bash的符號連結:
[[email protected] ~]# ls -l /bin/*sh-rwxr-xr-x. 1 root root 903272 Feb 22 05:09 /bin/bash-rwxr-xr-x. 1 root root 106216 Oct 17 2012 /bin/dashlrwxrwxrwx. 1 root root 4 Mar 22 10:22 /bin/sh -> bash
但在Mac OS上不是,/bin/sh和/bin/bash是兩個不同的檔案,儘管它們的大小隻相差100位元組左右:
iMac:~ wuxiao$ ls -l /bin/*sh-r-xr-xr-x 1 root wheel 1371648 6 Nov 16:52 /bin/bash-rwxr-xr-x 2 root wheel 772992 6 Nov 16:52 /bin/csh-r-xr-xr-x 1 root wheel 2180736 6 Nov 16:52 /bin/ksh-r-xr-xr-x 1 root wheel 1371712 6 Nov 16:52 /bin/sh-rwxr-xr-x 2 root wheel 772992 6 Nov 16:52 /bin/tcsh-rwxr-xr-x 1 root wheel 1103984 6 Nov 16:52 /bin/zsh
進階程式設計語言
理論上講,只要一門語言提供瞭解釋器(而不僅是編譯器),這門語言就可以勝任指令碼編程,常見的解釋型語言都是可以用作指令碼編程的,如:Perl、Tcl、Python、PHP、Ruby。Perl是最老牌的指令碼程式設計語言了,Python這些年也成了一些linux發行版的預置解譯器。
編譯型語言,只要有解譯器,也可以用作指令碼編程,如C shell是內建的(/bin/csh),Java有第三方解譯器Jshell,Ada有收費的解譯器AdaScript。
如下是一個PHP Shell Script樣本(假設檔案名稱叫test.php):
#!/usr/bin/php<?phpfor ($i=0; $i < 10; $i++) echo $i . "\n";執行:/usr/bin/php test.php或者:chmod +x test.php./test.php
如何選擇shell程式設計語言熟悉 vs 陌生
如果你已經掌握了一門程式設計語言(如PHP、Python、Java、JavaScript),建議你就直接使用這門語言編寫指令碼程式,雖然某些地方會有點囉嗦,但你能利用在這門語言領域裡的經驗(單元測試、單步調試、IDE、第三方類庫)。
新增的學習成本很小,只要學會怎麼使用shell解譯器(Jshell、AdaScript)就可以了。
簡單 vs 進階
如果你覺得自己熟悉的語言(如Java、C)寫shell指令碼實在太囉嗦,你只是想做一些備份檔案、安裝軟體、下載資料之類的事情,學著使用sh,bash會是一個好主意。
shell只定義了一個非常簡單的程式設計語言,所以,如果你的指令碼程式複雜度較高,或者要操作的資料結構比較複雜,那麼還是應該使用Python、Perl這樣的指令碼語言,或者是你本來就已經很擅長的進階語言。因為sh和bash在這方面很弱,比如說:
- ·它的函數只能返回字串,無法返回數組
- ·它不支援物件導向,你無法實現一些優雅的設計模式
- ·它是解釋型的,一邊解釋一邊執行,連PHP那種先行編譯都不是,如果你的指令碼包含錯誤(例如調用了不存在的函數),只要沒執行到這一行,就不會報錯
環境相容性
如果你的指令碼是提供給別的使用者使用,使用sh或者bash,你的指令碼將具有最好的環境相容性,perl很早就是linux標配了,python這些年也成了一些linux發行版的標配,至於mac os,它預設安裝了perl、python、ruby、php、java等主流程式設計語言。
第一個shell指令碼編寫
開啟文字編輯器,建立一個檔案,副檔名為sh(sh代表shell),副檔名並不影響指令碼執行,見名知意就好,如果你用php寫shell 指令碼,副檔名就用php好了。
輸入一些代碼,第一行一般是這樣:
#!/bin/bash#!/usr/bin/php“#!”是一個約定的標記,它告訴系統這個指令碼需要什麼解譯器來執行。
運行
運行Shell指令碼有兩種方法:
作為可執行程式
chmod +x test.sh./test.sh
注意,一定要寫成./test.sh,而不是test.sh,運行其它二進位的程式也一樣,直接寫test.sh,linux系統會去PATH裡尋找有沒有叫test.sh的,而只有/bin, /sbin, /usr/bin,/usr/sbin等在PATH裡,你的目前的目錄通常不在PATH裡,所以寫成test.sh是會找不到命令的,要用./test.sh告訴系統說,就在目前的目錄找。
通過這種方式運行bash指令碼,第一行一定要寫對,好讓系統尋找到正確的解譯器。
這裡的"系統",其實就是shell這個應用程式(想象一下Windows Explorer),但我故意寫成系統,是方便理解,既然這個系統就是指shell,那麼一個使用/bin/sh作為解譯器的指令碼是不是可以省去第一行呢?是的。
作為解譯器參數
這種運行方式是,直接運行解譯器,其參數就是shell指令碼的檔案名稱,如:
/bin/sh test.sh/bin/php test.php
這種方式啟動並執行指令碼,不需要在第一行指定解譯器資訊,寫了也沒用。
變數定義變數
定義變數時,變數名不加貨幣符號($),如:
your_name="qinjx"
注意,變數名和等號之間不能有空格,這可能和你熟悉的所有程式設計語言都不一樣。
除了顯式地直接賦值,還可以用語句給變數賦值,如:
for file in `ls /etc`
使用變數
使用一個定義過的變數,只要在變數名前面加貨幣符號即可,如:
your_name="qinjx"echo $your_nameecho ${your_name}
變數名外面的花括弧是可選的,加不加都行,加花括弧是為了協助解譯器識別變數的邊界,比如下面這種情況:
for skill in Ada Coffe Action Java; do echo "I am good at ${skill}Script"done
如果不給skill變數加花括弧,寫成echo "I am good at $skillScript",解譯器就會把$skillScript當成一個變數(其值為空白),代碼執行結果就不是我們期望的樣子了。
推薦給所有變數加上花括弧,這是個好的編程習慣。IntelliJ IDEA編寫shell script時,IDE就會提示加花括弧。
重定義變數
已定義的變數,可以被重新定義,如:
your_name="qinjx"echo $your_name your_name="alibaba"echo $your_name
這樣寫是合法的,但注意,第二次賦值的時候不能寫$your_name="alibaba",使用變數的時候才加美元符。
注釋
以“#”開頭的行就是注釋,會被解譯器忽略。
多行注釋
sh裡沒有多行注釋,只能每一行加一個#號。就像這樣:
#--------------------------------------------# 這是一個自動打ipa的指令碼,基於webfrogs的ipa-build書寫:https://github.com/webfrogs/xcode_shell/blob/master/ipa-build # 功能:自動為etao ios app打包,產出物為14個渠道的ipa包# 特色:全自動打包,不需要輸入任何參數#-------------------------------------------- ##### 使用者配置區 開始 ######## 項目根目錄,推薦將此指令碼放在項目的根目錄,這裡就不用改了# 應用程式名稱,確保和Xcode裡Product下的target_name.app名字一致###### 使用者配置區 結束 #####
如果在開發過程中,遇到大段的代碼需要臨時注釋起來,過一會兒又取消注釋,怎麼辦呢?每一行加個#符號太費力了,可以把這一段要注釋的代碼用一對花括弧括起來,定義成一個函數,沒有地方調用這個函數,這塊代碼就不會執行,達到了和注釋一樣的效果。
字串
字串是shell編程中最常用最有用的資料類型(除了數字和字串,也沒啥其它類型好用了,哈哈),字串可以用單引號,也可以用雙引號,也可以不用引號。單雙引號的區別跟PHP類似。
單引號
str=‘this is a string‘
單引號字串的限制:
- ·單引號裡的任何字元都會原樣輸出,單引號字串中的變數是無效的
- ·單引號字串中不能出現單引號(對單引號使用轉義符後也不行)
雙引號
your_name=‘qinjx‘str="Hello, I know your are \"$your_name\"! \n"
字串操作拼接字串
your_name="qinjx"greeting="hello, "$your_name" !"greeting_1="hello, ${your_name} !" echo $greeting $greeting_1
擷取字串長度:
string="abcd"echo ${#string} #輸出:4
提取子字串
string="alibaba is a great company"echo ${string:1:4} #輸出:liba
尋找子字串
string="alibaba is a great company"echo `expr index "$string" is`#輸出:8,這個語句的意思是:找出單詞is在這名話中的位置
數組管道條件判斷流程式控制制
和Java、PHP等語言不一樣,sh的流程式控制制不可為空白,如:<?phpif (isset($_GET["q"])) { search(q);}else { //do nothing}在sh/bash裡可不能這麼寫,如果else分支沒有語句執行,就不要寫這個else。還要注意,sh裡的if [ $foo -eq 0 ],這個方括弧跟Java/PHP裡if後面的圓括弧大不相同,它是一個可執行程式(和cd, ls, grep一樣),想不到吧?在CentOS上,它在/usr/bin目錄下:ll /usr/bin/[-rwxr-xr-x. 1 root root 33408 6月 22 2012 /usr/bin/[正因為方括弧在這裡是一個可執行程式,方括弧後面必須加空格,不能寫成if [$foo -eq 0]if elseifif conditionthen command1 command2 ... commandNfi寫成一行(適用於終端命令提示字元):if `ps -ef | grep ssh`; then echo hello; fi末尾的fi就是if倒過來拼字,後面還會遇到類似的if elseif conditionthen command1 command2 ... commandNelse commandfiif else-if elseif condition1then command1elif condition2 command2else commandNfifor whilefor在開篇的樣本裡示範過了:for var in item1 item2 ... itemNdo command1 command2 ... commandNdone寫成一行:for var in item1 item2 ... itemN; do command1; command2… done;
C風格的for
for (( EXP1; EXP2; EXP3 ))do command1 command2 command3donewhilewhile conditiondo commanddone無限迴圈while :do commanddone或者while truedo commanddone或者for (( ; ; ))untiluntil conditiondo commanddonecasecase "${opt}" in "Install-Puppet-Server" ) install_master $1 exit ;; "Install-Puppet-Client" ) install_client $1 exit ;; "Config-Puppet-Server" ) config_puppet_master exit ;; "Config-Puppet-Client" ) config_puppet_client exit ;; "Exit" ) exit ;; * ) echo "Bad option, please choose again"esaccase的文法和C family語言差別很大,它需要一個esac(就是case反過來)作為結束標記,每個case分支用右圓括弧,用兩個分號表示break
函數定義調用檔案包含
可以使用source和.關鍵字,如:source ./function.sh. ./function.sh在bash裡,source和.是等效的,他們都是讀入function.sh的內容並執行其內容(類似PHP裡的include),為了更好的可移植性,推薦使用第二種寫法。包含一個檔案和執行一個檔案一樣,也要寫這個檔案的路徑,不能光寫檔案名稱,比如上述例子中:. ./function.sh不可以寫作:. function.sh如果function.sh是使用者傳入的參數,如何獲得它的絕對路徑呢?方法是:real_path=`readlink -f $1`#$1是使用者輸入的參數,如function.sh. $real_path
使用者輸入執行指令碼時傳入指令碼運行中輸入select菜單stdin和stdout常用的命令
sh指令碼結合系統命令便有了強大的威力,在字元處理領域,有grep、awk、sed三劍客,grep負責找出特定的行,awk能將行拆分成多個欄位,sed則可以實現更新插入刪除等寫操作。
ps
查看進程列表
grep排除grep自身尋找與target相鄰的結果
Shell指令碼編程30分鐘入門