1. 程式人生 > >Shell程式設計-01-Shell指令碼初步入門

Shell程式設計-01-Shell指令碼初步入門

什麼是Shell

    簡單來說Shell其實就是一個命令直譯器,而它的作用就是解釋並執行使用者輸入的命令及程式。使用者每輸入一條命令,Shell就解釋執行一次。這種方式很容易讓大家想起在Windows環境中使用的command命令,我們在cmd視窗輸入一條命令,按下Enter鍵,則執行相應的命令和結果。     Shell位於作業系統的最外層,對外提供與使用者互動式的對話並返回相應的執行結果,對內則是將使用者輸入的命令解釋給作業系統。Shell在作業系統中所處的位置如下圖所示: 3-1 Shell在作業系統的位置.jpg

Shell在英文中的意思就是外殼、貝殼等,從圖中也可以看出,Shell就像殼一樣包住了系統的核心(Kernel)

Shell命令與Command命令對比 3-2 Shell命令與Command命令_c2i.jpg

什麼是Shell指令碼

    在理解了Shell之後,我們再來看看Shell指令碼。當命令或程式語句不是在命令列中執行時,而是通過程式檔案來執行時,該程式就稱之為Shell指令碼,我依然拿Windows來做比例。當我們需要執行比較少的命令時,我們可以一個一個命令的進行手動輸入,如果需要執行成百上千的命令時,你會怎麼辦?聰明的你肯定會脫口而出,用批處理(副檔名一般為bat或cmd)。其實Shell指令碼就類似於批處理,通過在指令碼中定義變數、執行命令、呼叫函式和邏輯判斷、迴圈等形成一個有機的整體,便形成一個功能強大、自動化程度較高的指令碼。

  • 在Windows通過批處理獲取系統資訊儲存為txt檔案,而後自動開啟該檔案,程式碼如下:
@echo off
set date=%date:~0,4%-%date:~5,2%-%date:~8,2%
echo "當前時間為:"%date%
cd /d "D:\"
mkdir SystemInfo
cd /d "SystemInfo"
systeminfo>systeminfo%date%.txt
start systeminfo%date%.txt
pause
  • Shell指令碼判斷當前登入使用者是否為root
# !/bin/bash
currentName=`whoami`
echo $currentName
if [ "$currentName" = "root" ]
  then
    echo "Current Login User is root"
else
  echo "Current Login User is :"$currentName
fi

Shell指令碼語言的種類

    Shell 指令碼語言是弱型別語言,即無須定義變數型別即可使用。在UNIX/Linux中主要有兩大類Shell:Bourne ShellC Shell

Bourne Shell

    Bourne Shell包括Bourne Shell(sh)、Korn Shell(ksh)、Bourne Again Shell(bash)三種類型。

  • Bourne Shell   由AT&T的Steve Bourne開發,是標準的UNIX Shell,很多UNIX系統都配有sh。

  • Korn Shell(ksh)   由David Korn開發,是Bournd Shell(sh)的超集合並且添加了csh引入的新功能,是目前很多UNIX系統標配的Shell,這些系統上的/bin/sh往往指向/bin/ksh的符號連結

  • Bourne Again Shell(bash)   由GNU專案組開發,主要目標是與POSIX標準操持一致,同時相容sh。bash從csh和ksh借鑑了很多功能,是各種Linux發行版本預設配置的Shell。Linux系統上的/bin/sh往往是指向/bin/bash的符號連結。但bash和sh還是有很多不同之處,雖然bash擴充套件了一些命令和引數,但bash並不完全相容sh,兩者之間有些行為並不一致。在大多數情況下區別不太大,有時還可以使用bash替代sh。

C Shell

    C Shell包括csh和tcsh兩種。csh由Berkeley大學開發,隨之BSD UNIX釋出,它的流程控制語句很像C語言,支援很多Bourne Shell所不支援的功能,如作業控制、別名、系統算術、命令歷史、命令列編輯等。tcsh是csh的增強版,加入了命令補全等功能,在FreeBSD、Mac OS X等系統上代替了csh。     以上介紹的這些Shell中,較為通用的是標準的Bourne Shell(sh)和C Shell(csh),而其中Bourne Shell(sh)已經被Bourne Again Shell(bash)所取代。可通過以下命令檢視CentOS 7.3系統Shell的支援情況。

[[email protected] tmp]$ cat /etc/shells
/bin/sh             #Linux常用的Shell,指向/bin/bash
/bin/bash           #Linux常用的Shell,也是預設使用的Shell
/sbin/nologin       #Linux常用的Shell,用於禁止使用者登入
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
/bin/tcsh
/bin/csh

Linux系統中主流的Shell是bash,而bash是由Bourne Shell(sh)發展而來,同時bash還包含了csh和ksh的特色。因此大多數指令碼都可以不做修改即可在sh執行,如果使用sh後結果與預期有差異,可以嘗試用bash代替sh.

常用作業系統預設Shell

    在常用的作業系統中,Linux中預設的Shell是Bourne Again Shell(bash),Solaris和FreeBSD下預設的是Bourne Shell(sh),AIX下預設的是Korn Shell(ksh)。那麼問題來了,我們該如何檢視所使用系統的Shell?以CentOS為例檢視系統預設的Shell:

  • 方法一:
[[email protected] tmp]$ echo $SHELL
/bin/bash
  • 方法二:
[[email protected] tmp]$ grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

root使用者結尾的/bin/bash就是使用者登入後的Shell直譯器。後續文章中重點講解的是Bourne Again Shell(bash)

Shell 指令碼的建立和執行

Shell指令碼的建立

    在Linux系統中,Shell指令碼通常是在編輯器vi/vim中進行編寫。可由UNIX/Linux命令、bash shell命令、程式結構控制語句、註釋等組成,推薦使用vim。

  • Shell指令碼開頭(第一行)   一個規範標準的Shell指令碼會在第一行指出由哪個直譯器來執行指令碼中的內容,一般如下所示:
#!/bin/bash
或
#!/bin/sh

注意事項:

1、第一行一般要求小於255個字元。 2、#!/bin/bash不是註釋,在執行指令碼時,核心會根據#!後的直譯器確定使用哪個直譯器來執行指令碼的內容。 3、這一行必須位於每個指令碼頂端的第一行,如果不是第一行則是代表註釋

#!/bin/bash
echo "bash test"
#!/bin/bash #代表該行是註釋
#!/bin/sh   #代表該行是註釋
  • bash和sh的區別   早期的bash與sh稍有不同,bash包含csh和ksh的特色,但大多數的指令碼都可以直接在sh上執行。 3-3 bash和sh區別.jpg

從上圖可以看到sh為bash的軟連結,大多數情況下,指令碼開頭使用#!/bin/bash和#!/bin/sh是沒有區別的。但還是建議採用#!/bin/bash

  一般情況下,安裝完Linux系統之後會自動安裝好bash軟體,檢視bash版本如下所示:

[[email protected] etc]$ cat /etc/redhat-release
CentOS Linux release 7.3.1611 (Core) #當前系統版本
[[email protected] etc]$ bash --version
GNU bash, version 4.2.46(1)-release (x86_64-redhat-linux-gnu) # bash 版本,後續省略自由軟體提示資訊

如果想體驗更高版本的bash,升級方法如下所示:

yum -y update bash #線上升級
rpm -qa bash  #檢視bash安裝包
bash-4.2.46-20.el7_2.x86_64

  以下是常用指令碼開頭的寫法,不同語言的指令碼在開頭一般都要加上如下標識內容:

#!/bin/sh
#!/bin/bash
#!/usr/bin/awk
#!/bin/sed
#!/usr/bin/tcsh
#!/usr/bin/perl

    CentOS中預設的Shell均為bash。因此即在指令碼中未加#!/bin/bash,它也會使用bash去解釋。如果不希望使用系統預設的Shell直譯器,就需要自行指定直譯器。建議大家一開始就養成好習慣,遵循Shell程式設計規範,在開頭第一行指定所使用的直譯器     如果在開頭未指定直譯器,要使用對應的直譯器來執行指令碼時,可以使用如下方法:

Shell指令碼: bash test.sh或sh test.sh
Python指令碼:python test.py
  • 指令碼註釋

    在很多程式語言中,都會支援單行和多行註釋,方便閱讀和維護,在Shell中,使用#對所在行進行註釋,註釋的內容並不會當作命令執行。註釋可單獨一行也可以緊跟在命令後面。建議在寫指令碼新增必要的註釋,方便自己也方便後續維護者或使用者。

註釋中儘量不要使用中文,指令碼中也儘量不要使用中文

Shell指令碼的執行

  • Shell指令碼的執行流程     當指令碼執行時,它會先查詢系統環境變數ENV,該變數指定了環境檔案(載入順序通常是/etc/profile、~/.bash_profile、~/.bashrc、/etc/bashrc等),在載入了上述環境變數檔案後,Shell開始執行Shell指令碼中的內容。     Shell指令碼執行的順序是從上到下,從左到右依次執行每一行的命令及語句。如果Shell中存在指令碼巢狀(子指令碼)時,就會執行巢狀指令碼的內容,完成後再返回父指令碼繼續執行父指令碼內後續的命令和語句。通常情況下,執行Shell指令碼時,會向系統核心啟動一個新的程序,以便在該程序中執行指令碼的命令和子指令碼,其流程圖如下所示: 3-4 Shell指令碼執行基本流程圖_c2i.jpg

  • Shell指令碼的執行方式

【1】bash script-name或sh script-name     這種方式是當指令碼檔案本身沒有可執行許可權(即檔案屬性沒有x佔位符)時常使用的方式或指令碼檔案沒有指定直譯器時常用的方法。 3-5 Shell執行方式-1.jpg

【2】path/script-name或./script-name     這種方式是指在當前路徑下執行指令碼,前提是指令碼必須有可執行許可權,具體方法為chmod +x script-name。然後通過相對路徑或絕對路徑執行指令碼。 3-6 Shell執行方式-2.jpg

【3】source script-name或. script-name     這種方法通常使用source或" . "讀入或載入指定的Shell指令碼,如son.sh,然後依次執行指定的Shell指令碼檔案son.sh中的所有語句。這些語句將在當前父Shell指令碼father.sh中執行(其他幾種模式都會啟動新的程序執行子指令碼)。

使用source或" . "可以將son.sh自身指令碼中的變數值或函式等的返回值傳遞到當前父Shell指令碼father.sh中使用,這是和其他兩種方法最大的區別,因此需要特別注意。

3-7 Shell執行方式-3_c2i.jpg

【4】sh<script-name或cat script-name | sh     這種方法同樣適用於bash,這種方法並不常見,瞭解知道即可。其原理就是利用了管道技術。

3-8 Shell執行方式-4.jpg

  • 示例

大家可以看看以下指令碼的正確答案是哪一個?

3-9 Shell執行示例-1.jpg

參考的答案選項如下所示:

  • [ ] 當前使用者
  • [ ] admin
  • [ ] 無內容輸入

正確答案是無內容輸入。原因可檢視Shell指令碼的幾種執行方式。

通過這個示例我們可以得出如下結論:

  • 子Shell指令碼會直接繼承父Shell的變數、函式等,如兒子繼承父親基因。
  • 如果希望父Shell繼承子Shell的變數,就要使用source或" . "

3-10 Shell執行示例-2.jpg

指令碼規範

    每種語言都有自己的開發規範,雖然不是強制遵守,但有規範的程式碼不便方便閱讀、維護、多人協同開發,同時也能減少出現Bug的概率。主要的規範如下所示:

  • 【1】Shell指令碼的第一行指定指令碼直譯器
#!/bin/bash
或
#!/bin/sh
  • 【2】Shell指令碼的開關新增版本、版權、作者等
#Date:2017-11-29 22:50
#Author:Surpassme
#Description:This is sample shell scripts
#Version:1.5
  • 【3】Shell指令碼中儘量不要使用中文   雖說Linux也能相容中文,但還是存在切換系統環境後中文出現亂碼的問題。如果非要用中文,可對系統進行字符集調整。如export LANG="zh_CN.UTF-8",並在指令碼中重新定義字符集設定和系統保持一致。

  • 【4】Shell指令碼儘量新增副檔名.sh

  • 【5】養成良好的指令碼書寫習慣
1、成對的符號儘量一次性寫全,防止遺漏
2、中括號([])兩端至少要保留一個空格。
3、流程控制語句,應一次性將格式寫完,再新增內容
4、良好的程式碼縮排,方便閱讀
5、指令碼的各個符號必須為英文狀態下的符號
6、常規變數的字串定義時應加雙引號("")並且等號前後均不能有空格,需要強引用(指所見即所得的字串引用),則使用單引號(''),如果是命令引用,則用反引號(``)