shell腳本專題(01):探祕腳本首行

一、專題背景

最近使用了個自動化平臺(詳見自動化運維平臺Spug測試)進行每週的變更,效果很不錯,平臺將大量重複繁瑣的操作通過腳本分發方式標準化自動化了,平臺核心是下發到各個服務器的shell腳本,感覺有必要對shell腳本做個總結,所以有了寫本專題的想法。本專題將結合運維實際介紹shell腳本的各項用法,預計10篇左右,將包括系統巡檢、監控、ftp上傳下載、數據庫查詢、日誌清理、時鐘同步、定時任務等,裏面會涉及shell常用語法、注意事項、調試排錯等。

二、本文前言

本文是該專題的第一篇。

做運維的都寫過腳本,腳本的第一行#!/bin/bash大家都很熟悉,今天就具體講講這個第一行:

  • 爲什麼用使用#!/bin/bash
  • 首行不寫或者寫得有誤有什麼影響

在具體測試前先介紹下shell和bash

三、什麼是shell

shell是一種特殊的交互式工具。它爲用戶提供了啓動程序、管理文件系統中的文件以及運行在Linux系統上的進程的途徑。shell的核心是命令行提示符。命令行提示符是shell負責交互的部分。它允許你輸入文本命令,然後解釋命令,並在內核中執行。
當一個用戶登錄Linux系統之後,系統初始化程序init就爲每一個用戶運行一個稱爲shell(外殼)的程序。shell就是一個命令行解釋器,它爲用戶提供了一個向Linux內核發送請求以便運行程序的界面系統級程序,用戶可以用shell來啓動、掛起、停止甚至是編寫一些程序。 

image-20210111143520721

四、什麼是bash

在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和dash的區別(後面的測試基於二者的區別):dash shell只是Bourne shell功能的一個子集, bash shell腳本中的有些功能沒法在dash shell中使用,如在腳本中dash無法使用雙等號(==)來測試兩個字符串是否相等,可以利用這個特性區分bash和dash。

五、不寫第一行可以不

爲了說清楚這個問題,先看兩個例子

示例一:

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

image-20210108142453618

腳本名 首行 執行方式 執行結果
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

image-20210108150618076

腳本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

1.默認的交互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

image-20210108152549649

默認的交互shell爲bash

默認的交互shell由配置文件/etc/default/useradd的SHELL參數決定,感興趣的童鞋可以修改測試下。

2.默認的系統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*

image-20210108152648738

默認的系統shell /bin/sh指向/bin/dash,即默認系統shell爲dash。

3.腳本第一行測試總結

介紹完了默認交互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!

image-20210108162623528

示例三執行彙總:

腳本名 首行 執行方式 默認交互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專題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章