最近使用了個自動化平臺(詳見自動化運維平臺Spug測試)進行每週的變動,效果很不錯,平臺將大量重複繁瑣的操做經過腳本分發方式標準化自動化了,平臺核心是下發到各個服務器的shell腳本,感受有必要對shell腳本作個總結,因此有了寫本專題的想法。本專題將結合運維實際介紹shell腳本的各項用法,預計10篇左右,將包括系統巡檢、監控、ftp上傳下載、數據庫查詢、日誌清理、時鐘同步、定時任務等,裏面會涉及shell經常使用語法、注意事項、調試排錯等。html
本文是該專題的第一篇。shell
作運維的都寫過腳本,腳本的第一行#!/bin/bash
你們都很熟悉,今天就具體講講這個第一行:數據庫
- 爲何用使用#!/bin/bash
- 首行不寫或者寫得有誤有什麼影響
在具體測試前先介紹下shell和bashubuntu
shell是一種特殊的交互式工具。它爲用戶提供了啓動程序、管理文件系統中的文件以及運行在Linux系統上的進程的途徑。shell的核心是命令行提示符。命令行提示符是shell負責交互的部分。它容許你輸入文本命令,而後解釋命令,並在內核中執行。 當一個用戶登陸Linux系統以後,系統初始化程序init就爲每個用戶運行一個稱爲shell(外殼)的程序。shell就是一個命令行解釋器,它爲用戶提供了一個向Linux內核發送請求以便運行程序的界面系統級程序,用戶能夠用shell來啓動、掛起、中止甚至是編寫一些程序。
在Linux系統上,一般有好幾種Linux shell可用。不一樣的shell有不一樣的特性,有些更利於建立腳本,有些則更利於管理進程。全部Linux發行版默認的交互shell都是bash shell。bash shell由GNU項目開發,被看成標準Unix shell——Bourne shell(以建立者的名字命名)的替代品。bash shell的名稱就是針對Bourne shell的拼寫所玩的一個文字遊戲,稱爲Bourne again shell。
除了bash shell,還有dash shell、zsh shell、tcsh、ash等。bash
bash和dash的區別(後面的測試基於兩者的區別):dash shell只是Bourne shell功能的一個子集, bash shell腳本中的有些功能無法在dash shell中使用,如在腳本中dash沒法使用雙等號(==)來測試兩個字符串是否相等,能夠利用這個特性區分bash和dash。服務器
爲了說清楚這個問題,先看兩個例子運維
示例一:ide
root@ubuntu1604:~# more first.sh echo "Hello World" root@ubuntu1604:~# ll|grep first.sh -rw-r--r-- 1 root root 19 Jan 8 14:19 first.sh root@ubuntu1604:~# sh first.sh Hello World root@ubuntu1604:~# ./first.sh -bash: ./first.sh: Permission denied root@ubuntu1604:~# chmod u+x first.sh root@ubuntu1604:~# ./first.sh Hello World
腳本名 | 首行 | 執行方式 | 執行結果 |
---|---|---|---|
first.sh | 空 | ./ | 成功 |
腳本first.sh只有一行:echo "Hello World",腳本執行成功,那是否是意味着首行聲明能夠不須要寫呢,或者說首行的聲明會給腳本執行形成什麼影響?咱們再看個示例。工具
示例二:測試
root@ubuntu1604:~# more dash1.sh test1=abcdef test2=abcdef if [ $test1 == $test2 ] then echo "They're the same!" else echo "They're different" fi root@ubuntu1604:~# ./dash1.sh They're the same! root@ubuntu1604:~# more dash2.sh #!/bin/bash test1=abcdef test2=abcdef if [ $test1 == $test2 ] then echo "They're the same!" else echo "They're different" fi root@ubuntu1604:~# ./dash2.sh They're the same! root@ubuntu1604:~# more dash3.sh #!/bin/dash test1=abcdef test2=abcdef if [ $test1 == $test2 ] then echo "They're the same!" else echo "They're different" fi root@ubuntu1604:~# ./dash3.sh ./dash3.sh: 4: [: abcdef: unexpected operator They're different root@ubuntu1604:~# more dash4.sh #!/bin/sh test1=abcdef test2=abcdef if [ $test1 == $test2 ] then echo "They're the same!" else echo "They're different" fi root@ubuntu1604:~# ./dash4.sh ./dash4.sh: 4: [: abcdef: unexpected operator They're different
腳本dash1.sh、dash2.sh、dash3.sh和dash4.sh的執行方式相同,內容也相同(除了首行聲明外),結果執行結果卻大不相同,dash1.sh、dash2.sh執行正常,dash3.sh和dash4.sh執行報錯。這個至少證實首行的聲明仍是起做用的。
到這裏你們確定雲裏霧裏了,即便以前對shell腳本很清楚的童鞋估計如今也被我繞暈了,這就對了,由於我就是這麼過來了……
說回正題,咱們先梳理下首行空、/bin/bash、/bin/dash和/bin/sh這4種狀況的執行狀況,你們先看一張表:
腳本名 | 首行 | 執行方式 | 執行結果 |
---|---|---|---|
dash1.sh | 空 | ./ | 成功 |
dash2.sh | #!/bin/bash | ./ | 成功 |
dash3.sh | #!/bin/dash | ./ | 失敗 |
dash4.sh | #!/bin/sh | ./ | 失敗 |
爲了解釋這個緣由,先介紹下默認的交互shell和默認的系統shell
默認的交互shell會在用戶登陸某個虛擬控制檯終端或在GUI中運行終端仿真器時啓動,簡單講就是用戶使用登錄交互終端如crt、putty等登錄系統的默認shell。
root@ubuntu1604:~# echo $SHELL /bin/bash root@ubuntu1604:~# cat /etc/passwd|grep root root:x:0:0:root:/root:/bin/bash
默認的交互shell爲bash
默認的交互shell由配置文件/etc/default/useradd的SHELL參數決定,感興趣的童鞋能夠修改測試下。
默認的系統shell,用於那些須要在啓動時使用的系統shell腳本,「sh+腳本名」這種執行方式就是使用的系統shell,後面會有介紹。
root@ubuntu1604:~# which sh /bin/sh root@ubuntu1604:~# cd /bin/ root@ubuntu1604:/bin# ll|grep sh -rwxr-xr-x 1 root root 1037528 May 16 2017 bash* -rwxr-xr-x 1 root root 253816 Jun 16 2017 btrfs-show-super* -rwxr-xr-x 1 root root 154072 Feb 18 2016 dash* lrwxrwxrwx 1 root root 4 Feb 20 2019 rbash -> bash* lrwxrwxrwx 1 root root 4 Feb 20 2019 sh -> dash* lrwxrwxrwx 1 root root 4 Feb 20 2019 sh.distrib -> dash* lrwxrwxrwx 1 root root 7 Aug 19 2015 static-sh -> busybox*
默認的系統shell /bin/sh指向/bin/dash,即默認系統shell爲dash。
介紹完了默認交互shell和默認系統shell,在對以上兩個示例作個總結:
腳本名 | 首行 | 執行方式 | 默認交互shell | 默認系統shell | 等效於 | 執行結果 |
---|---|---|---|---|---|---|
first.sh | 空 | ./ | bash | dash | bash first.sh | 成功 |
dash1.sh | 空 | ./ | bash | dash | bash dash1.sh | 成功 |
dash2.sh | #!/bin/bash | ./ | bash | dash | bash dash1.sh | 成功 |
dash3.sh | #!/bin/dash | ./ | bash | dash | dash dash1.sh | 失敗 |
dash4.sh | #!/bin/sh | ./ | bash | dash | dash dash1.sh | 失敗 |
你們如今可能有些頭緒了,經過實驗能夠得出結論一:
- 1.當腳本沒有在首行聲明shell時,系統會使用默認的交互shell(即文中的bash)執行腳本;
- 2.當腳本首行有聲明shell時,系統會讀取對應的shell;
回到以前的問題:不寫第一行能夠不?
答案是不寫首行聲明某些時候不影響腳本執行結果,可是爲了規範,建議你們最好養成首行就聲明shell的習慣,由於首行 #後面的驚歎號會告訴shell用哪一個shell來運行腳本,而且聲明只能在首行。
在一般的shell腳本中,井號( # )用做註釋行。shell並不會處理shell腳本中的註釋行。然而,shell腳本文件的第一行是個例外, # 後面的驚歎號會告訴shell用哪一個shell來運行腳本。
shell腳本執行有兩種方式,「sh+腳本名」和「./腳本名」,這兩種方式有啥卻別呢?首行若是聲明有誤或者在第二行聲明shell有什麼差異沒?看看下面的例子:
示例三:
root@ubuntu1604:~# more sh1.sh echo "Hello,I am loong576!" root@ubuntu1604:~# sh sh1.sh Hello,I am loong576! root@ubuntu1604:~# ./sh1.sh Hello,I am loong576! root@ubuntu1604:~# more sh2.sh #!/bin/bash echo "Hello,I am loong576!" root@ubuntu1604:~# sh sh2.sh Hello,I am loong576! root@ubuntu1604:~# ./sh2.sh Hello,I am loong576! root@ubuntu1604:~# more sh3.sh #!/bin/dash echo "Hello,I am loong576!" root@ubuntu1604:~# sh sh3.sh Hello,I am loong576! root@ubuntu1604:~# ./sh3.sh Hello,I am loong576! root@ubuntu1604:~# more sh4.sh #!/bin/errorsh echo "Hello,I am loong576!" root@ubuntu1604:~# sh sh4.sh Hello,I am loong576! root@ubuntu1604:~# ./sh4.sh -bash: ./sh4.sh: /bin/errorsh: bad interpreter: No such file or directory root@ubuntu1604:~# more sh5.sh #!/bin/errorsh echo "Hello,I am loong576!" root@ubuntu1604:~# sh sh5.sh Hello,I am loong576! root@ubuntu1604:~# ./sh5.sh Hello,I am loong576!
示例三執行彙總:
腳本名 | 首行 | 執行方式 | 默認交互shell | 默認系統shell | 等效於 | 執行結果 |
---|---|---|---|---|---|---|
sh1.sh | 空 | sh | bash | dash | dash sh1.sh | 成功 |
sh1.sh | 空 | ./ | bash | dash | bash sh1.sh | 成功 |
sh2.sh | #!/bin/bash | sh | bash | dash | dash sh2.sh | 成功 |
sh2.sh | #!/bin/bash | ./ | bash | dash | bash sh2.sh | 成功 |
sh3.sh | #!/bin/dash | sh | bash | dash | dash sh3.sh | 成功 |
sh3.sh | #!/bin/dash | ./ | bash | dash | dash sh3.sh | 成功 |
sh4.sh | shell定義有誤 | sh | bash | dash | dash sh4.sh | 成功 |
sh4.sh | shell定義有誤 | ./ | bash | dash | errorsh sh4.sh | 失敗 |
sh5.sh | 空,第二行定義且有誤 | sh | bash | dash | dash sh5.sh | 成功 |
sh5.sh | 空,第二行定義且有誤 | ./ | bash | dash | bash sh5.sh | 成功 |
以上實驗驗證了以前的結論一,也可得出shell聲明須在首行的結論;同時從實驗也可對比sh和./兩種方式的區別得出結論二:
- 1.使用sh方式時,即便不管腳本首行指定的shell是什麼或者爲空都不影響腳本執行時使用的shell,具體講在本文「sh+腳本」方式等價於「dash+腳本」;
- 2.使用./執行腳本時會讀取腳本開頭指定的shell,若首行未指定shell則使用默認的交互shell,即本文的bash;
固然,兩者還有個小區別是sh能夠直接運行,./方式須要腳本有執行權限。
本文圍繞腳本第一行展開測試,得出首行聲明有必要且必須在首行的結論,同時也擴展對腳本的兩種執行方式進行了對比,除了測試,也對shell和bash進行了理論介紹。
好了,腳本第一行寫好了,下面能夠編寫各個功能的shell腳本了,敬請期待下篇,你們快上車,關注不迷路,哈哈……
更多請關注:shell專題