1. 程式人生 > >Linux學習筆記26——什麼是 Shell scripts、shell script練習及講解

Linux學習筆記26——什麼是 Shell scripts、shell script練習及講解

如果你真的很想要走資訊這條路,並且想要管理好屬於你的主機,那麼,別說鳥哥不告訴你, 可以自動管理系統的好工具: Shell scripts!這傢伙真的是得要好好學習學習的! 基本上, shell script 有點像是早期的批處理檔案,亦即是將一些指令彙整起來一次執行,但是Shell script 擁有更強大的功能,那就是他可以進行類似程式 (program) 的撰寫,並且不需要經過編譯 (compile) 就能夠執行(解釋型語言,注意,js也是編譯型語言,但是命令列的像shell是解釋型), 真的很方便。加上我們可通過 shell script 來簡化我們日常的工作管理, 而且,整個 Linux 環境中,一些服務 (services) 的啟動都是通過 shellscript 的, 如果你對於 script 不瞭解,嘿嘿!發生問題時,可真是會求助無門喔!所以,好好的學一學他吧!

 

一、什麼是 Shell scripts

 

什麼是 shell script (程式化指令碼) 呢?就字面上的意義,我們將他分為兩部份。 在“ shell”部分,我們在 十章的 BASH 當中已經提過了,那是一個命令列下面讓我們與系統溝通的一個工具介面。那麼“ script ”是啥? 字面上的意義, script 是“指令碼、劇本”的意思。整句話是說,shell script 是針對 shell 所寫的“劇本!”

什麼東西啊?其實, shell script 是利用 shell 的功能所寫的一個“程式 (program)”,這個程式是使用純文字檔案,將一些 shell 的語法與指令(含外部指令)寫在裡面, 搭配正則表示式、管線命令與資料流重導向等功能,以達到我們所想要的處理目的。

所以,簡單的說, shell script 就像是早期 DOS 年代的批處理檔案 (.bat) ,最簡單的功能就是將許多指令彙整寫在一起, 讓使用者很輕易的就能夠 one touch 的方法去處理複雜的動作 (執行一個檔案 "shell script" ,就能夠一次執行多個指令)。 而且 shell script 更提供陣列、迴圈、條件與邏輯判斷等重要功能,讓使用者也可以直接以 shell 來撰寫程式,而不必使用類似 C 程式語言等傳統程式撰寫的語法呢!

這麼說你可以瞭解了嗎?是的! shell script 可以簡單的被看成是批處理檔案, 也可以被說成是一個程式語言,且這個程式語言由於都是利用 shell 與相關工具指令, 所以不需要編譯即可執行,且擁有不錯的除錯 (debug) 工具(shell的debug沒怎麼用過,pl/sql的debug不會用。。還是eclipse的debug用得習慣),所以,他可以幫助系統管理員快速的管理好主機。

1,幹嘛學習 shell scripts

這是個好問題:“我又幹嘛一定要學 shell script ?我又不是資訊人,沒有寫程式的概念, 那我幹嘛還要學 shell script 呢?不要學可不可以啊?”呵呵~如果 Linux 對你而言, 你只是想要“會用”而已,那麼,不需要學 shell script 也還無所謂,這部分先給他跳過去, 等到有空的時候,再來好好的瞧一瞧。但是,如果你是真的想要玩清楚 Linux 的來龍去脈, 那麼 shellscript 就不可不知,為什麼呢?因為:(其實對我來說,就是能管伺服器咯,順便弄弄ngnix,裝裝hadoop)

自動化管理的重要依據

 

不用鳥哥說你也知道,管理一部主機真不是件簡單的事情,每天要進行的任務就有:查詢登入檔案、追蹤流量、監控使用者使用主機狀態、主機各項硬體裝置狀態、 主機軟體更新查詢、更不要說得應付其他使用者的突然要求了。而這些工作的進行可以分為: (1)自行手動處理,或是 (2)寫個簡單的程式來幫你每日“自動處理分析”這兩種方式,你覺得哪種方式比較好? 當然是讓系統自動工作比較好,對吧!呵呵~這就得要良好的 shell script 來幫忙的啦!

追蹤與管理系統的重要工作

 

雖然我們還沒有提到服務啟動的方法,不過,這裡可以先提一下,我們 CentOS 6.x 以前的版本中,系統的服務 (services) 啟動的介面是在 /etc/init.d/ 這個目錄下,目錄下的所有檔案都是 scripts ; 另外,包括開機 (booting) 過程也都是利用 shell script 來幫忙搜尋系統的相關設定資料, 然後再代入各個服務的設定引數啊!舉例來說,如果我們想要重新啟動系統登入檔案, 可以使用:“/etc/init.d/rsyslogd restart”,那個 rsyslogd 檔案就是 script 啦!

另外,鳥哥曾經在某一代的 Fedora 上面發現,啟動 MySQL 這個資料庫服務時,確實是可以啟動的, 但是螢幕上卻老是出現“failure”!後來才發現,原來是啟動 MySQL 那個 script 會主動的以“空的密碼”去嘗試登陸 MySQL ,但為了安全性鳥哥修改過 MySQL 的密碼囉~當然就登陸失敗~ 後來改了改 script ,就略去這個問題啦!如此說來, script 確實是需要學習的啊!

時至今日,雖然 /etc/init.d/* 這個指令碼啟動的方式 (systemV) 已經被新一代的 systemd 所取代 (從 CentOS 7 開始), 但是很多的個別服務在管理他們的服務啟動方面,還是使用 shellscript 的機制喔!所以,最好還是能夠熟悉啦!

簡單入侵偵測功能

當我們的系統有異狀時,大多會將這些異狀記錄在系統記錄器,也就是我們常提到的“系統登入檔案”, 那麼我們可以在固定的幾分鐘內主動的去分析系統登入檔案,若察覺有問題,就立刻通報管理員, 或者是立刻加強防火牆的設定規則,如此一來,你的主機可就能夠達到“自我保護”的聰明學習功能啦~ 舉例來說,我們可以通過 shell script 去分析“當該封包嘗試幾次還是連線失敗之後,就予以抵擋住該 IP”之類的舉動,例如鳥哥寫過一個關於抵擋砍站軟體的shell script , 就是用這個想法去達成的呢!

連續指令單一化

其實,對於新手而言, script 最簡單的功能就是:“彙整一些在 command line 下達的連續指令,將他寫入 scripts 當中,而由直接執行 scripts 來啟動一連串的 command line 指令輸入!”例如: 防火牆連續規則 (iptables),開機載入程式的專案 (就是在 /etc/rc.d/rc.local裡頭的資料) ,等等都是相似的功能啦! 其實,說穿了,如果不考慮 program 的部分,那麼scripts 也可以想成“僅是幫我們把一大串的指令彙整在一個檔案裡面, 而直接執行該檔案就可以執行那一串又臭又長的指令段!”就是這麼簡單啦!

簡易的資料處理

 

由前一章正則表示式的 awk 程式說明中, 你可以發現, awk 可以用來處理簡單的資料資料呢!例如薪資單的處理啊等等的。 shell script 的功能更強大,例如鳥哥曾經用 shell script 直接處理資料資料的比對啊, 文字資料的處理啊等等的,撰寫方便,速度又快(因為在 Linux效能較佳),真的是很不錯用的啦!

舉例來說,鳥哥每學期都得要以學生的學號來建立他們能夠操作 Linux 的系統帳號,然後每個帳號還得要能夠有磁碟容量的限制 (quota) 以及相關的設定等等, 那因為學校的校務系統提供的資料都是一整串學生資訊,並沒有單純的學號欄位,所以鳥哥就得要通過前幾章的方法搭配 shell script 來自動處理相關設定流程, 這樣才不會每學期都頭疼一次啊!

跨平臺支援與學習歷程較短

 

幾乎所有的 Unix Like 上面都可以跑 shell script ,連 MS Windows 系列也有相關的 script 模擬器可以用, 此外, shell script 的語法是相當友好的,看都看的懂得文字 (雖然是英文),而不是機器碼, 很容易學習~這些都是你可以加以考慮的學習點啊!

上面這些都是你考慮學習 shell script 的特點~此外, shell script 還可以簡單的以 vim 來直接編寫,實在是很方便的好東西!所以,還是建議你學習一下啦。

不過,雖然 shell script 號稱是程式 (program) ,但實際上, shell script 處理資料的速度上是不太夠的(在學習和使用shell是,應該對比下python,python的指令碼也可以實現很多功能)。因為 shell script 用的是外部的指令與 bash shell 的一些預設工具,所以,他常常會去呼叫外部的函式庫,因此,運算速度上面當然比不上傳統的程式語言。 所以囉, shellscript 用在系統管理上面是很好的一項工具,但是用在處理大量數值運算上, 就不夠好了,因為 Shell scripts 的速度較慢,且使用的 CPU 資源較多,造成主機資源的分配不良。還好,我們通常利用 shell script 來處理伺服器的偵測,倒是沒有進行大量運算的需求啊!所以不必擔心的啦!

2,第一支 script 的撰寫與執行

 

如同前面講到的,shell script 其實就是純文字檔案,我們可以編輯這個檔案,然後讓這個檔案來幫我們一次執行多個指令, 或者是利用一些運算與邏輯判斷來幫我們達成某些功能。所以啦,要編輯這個檔案的內容時,當然就需要具備有 bash 指令下達的相關認識。下達指令需要注意的事項在第四章的開始下達指令小節內已經提過,有疑問請自行回去翻閱。 在 shellscript 的撰寫中還需要用到下面的注意事項:

1. 指令的執行是從上而下、從左而右的分析與執行;

2. 指令的下達就如同第四章內提到的: 指令、選項與引數間的多個空白都會被忽略掉;

3. 空白行也將被忽略掉,並且 [tab] 按鍵所推開的空白同樣視為空白鍵;

4. 如果讀取到一個 Enter 符號 (CR) ,就嘗試開始執行該行 (或該串) 命令;

5. 至於如果一行的內容太多,則可以使用“ [Enter] ”來延伸至下一行;

6. “ # ”可做為註解!任何加在 # 後面的資料將全部被視為註解文字而被忽略!

 

如此一來,我們在 script 內所撰寫的程式,就會被一行一行的執行。現在我們假設你寫的這個程式檔名是 /home/dmtsai/shell.sh 好了,那如何執行這個檔案?很簡單,可以有下面幾個方法:

直接指令下達: shell.sh 檔案必須要具備可讀與可執行 (rx) 的許可權,然後:

絕對路徑:使用 /home/dmtsai/shell.sh 來下達指令;

相對路徑:假設工作目錄在 /home/dmtsai/ ,則使用 ./shell.sh 來執行

變數“PATH”功能:將 shell.sh 放在 PATH 指定的目錄內,例如: ~/bin/

以 bash 程式來執行:通過“ bash shell.sh ”或“ sh shell.sh ”來執行

反正重點就是要讓那個 shell.sh 內的指令可以被執行的意思啦! 咦!那我為何需要使用“./shell.sh ”來下達指令?忘記了嗎?回去第十章內的指令搜尋順序察看一下, 你就會知道原因了!同時,由於 CentOS 預設使用者主資料夾下的 ~/bin 目錄會被設定到 ${PATH} 內,所以你也可以將 shell.sh 建立在 /home/dmtsai/bin/ 下面 (~/bin 目錄需要自行設定) 。此時,若 shell.sh 在 ~/bin 內且具有 rx 的許可權,那就直接輸入 shell.sh 即可執行該指令碼程式!

那為何“ sh shell.sh ”也可以執行呢?這是因為 /bin/sh 其實就是 /bin/bash ( 連結檔案) ,使用 sh shell.sh 亦即告訴系統,我想要直接以 bash 的功能來執行 shell.sh 這個檔案內的相關指令的意思,所以此時你的 shell.sh 只要有 r 的許可權即可被執行喔!而我們也可以利用 sh 的引數,如 -n 及 -x 來檢查與追蹤 shell.sh 的語法是否正確呢! ^_^

 

撰寫第一支 script

在武俠世界中,不論是那個門派,要學武功要從掃地與蹲馬步做起,那麼要學程式呢?呵呵,肯定是由“秀出 Hello World!” 這個字眼開始的!OK!那麼鳥哥就先寫一支 script 給大家瞧一瞧:

[[email protected] ~]$ mkdir bin; cd bin

[[email protected] bin]$ vim hello.sh

#!/bin/bash

# Program:

# This program shows "Hello World!" in your screen.

# History:

# 2015/07/16 VBird First release

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin

export PATH

echo -e "Hello World! \a \n"

exit 0

 

在本章當中,請將所有撰寫的 script 放置到你主資料夾的 ~/bin 這個目錄內,未來比較好管理啦!上面的寫法當中,鳥哥主要將整個程式的撰寫分成數段,大致是這樣:

1. 第一行 #!/bin/bash 在宣告這個 script 使用的 shell 名稱: 因為我們使用的是 bash ,所以,必須要以“ #!/bin/bash ”來宣告這個檔案內的語法使用 bash 的語法!那麼當這個程式被執行時,他就能夠載入 bash 的相關環境配置檔案 ( 一般來說就是 non-login shell的 ~/.bashrc) , 並且執行 bash 來使我們下面的指令能夠執行!這很重要的!( 在很多狀況中,如果沒有設定好這一行, 那麼該程式很可能會無法執行,因為系統可能無法判斷該程式需要使用什麼 shell 來執行啊!)

2. 程式內容的說明: 整個 script 當中,除了第一行的“ #! ”是用來宣告 shell 的之外,其他的# 都是“註解”用途! 所以上面的程式當中,第二行以下就是用來說明整個程式的基本資料。一般來說, 建議你一定要養成說明該 script 的:1. 內容與功能; 2. 版本資訊; 3.作者與聯絡方式; 4. 建立日期;5. 歷史紀錄 等等。這將有助於未來程式的改寫與 debug呢!

3. 主要環境變數的宣告: 建議務必要將一些重要的環境變數設定好,鳥哥個人認為,PATH 與 LANG (如果有使用到輸出相關的資訊時) 是當中最重要的! 如此一來,則可讓我們這支程式在進行時,可以直接下達一些外部指令,而不必寫絕對路徑呢!比較方便啦!

4. 主要程式部分 就將主要的程式寫好即可!在這個例子當中,就是 echo 那一行啦!

5. 執行成果告知 (定義回傳值) 是否記得我們在第十章裡面要討論一個指令的執行成功與否,可以使用 $? 這個變數來觀察~ 那麼我們也可以利用 exit 這個指令來讓程式中斷,並且回傳一個數值給系統。 在我們這個例子當中,鳥哥使用 exit 0 ,這代表離開 script並且回傳一個 0 給系統, 所以我執行完這個 script 後,若接著下達 echo $? 則可得到 0的值喔! 更聰明的讀者應該也知道了,呵呵!利用這個 exit n (n 是數字) 的功能,我們還可以自訂錯誤訊息, 讓這支程式變得更加的 smart 呢!

接下來通過剛剛上頭介紹的執行方法來執行看看結果吧!

[[email protected] bin]$ sh hello.sh

Hello World !

 

你會看到螢幕是這樣,而且應該還會聽到“咚”的一聲,為什麼呢?還記得前一章提到的 printf吧?用 echo 接著那些特殊的按鍵也可以發生同樣的事情~ 不過, echo 必須要加上 -e 的選項才行!呵呵!在你寫完這個小 script 之後,你就可以大聲的說:“我也會寫程式了”!哈哈!很簡單有趣吧~ ^_^

另外,你也可以利用:“chmod a+x hello.sh; ./hello.sh”來執行這個 script 的呢!(要是不加的話,會報許可權不夠的錯誤)

3,撰寫 shell script 的良好習慣建立

一個良好習慣的養成是很重要的~大家在剛開始撰寫程式的時候,最容易忽略這部分, 認為程式寫出來就好了,其他的不重要。其實,如果程式的說明能夠更清楚,那麼對你自己是有很大的幫助的。

舉例來說,鳥哥自己為了自己的需求,曾經撰寫了不少的 script 來幫我進行主機 IP 的偵測啊、 登入檔案分析與管理啊、自動上傳下載重要配置檔案啊等等的,不過,早期就是因為太懶了, 管理的主機又太多了,常常同一個程式在不同的主機上面進行更改,到最後,到底哪一支才是最新的都記不起來, 而且,重點是,我到底是改了哪裡?為什麼做那樣的修改?都忘的一乾二淨~真要命~

所以,後來鳥哥在寫程式的時候,通常會比較仔細的將程式的設計過程給他記錄下來,而且還會記錄一些歷史紀錄, 如此一來,好多了~至少很容易知道我修改了哪些資料,以及程式修改的理念與邏輯概念等等, 在維護上面是輕鬆很多很多的喔!(只要寫程式,就要記得加上說明,為什麼這麼寫)。

另外,在一些環境的設定上面,畢竟每個人的環境都不相同,為了取得較佳的執行環境, 我都會自行先定義好一些一定會被用到的環境變數,例如 PATH 這個玩意兒! 這樣比較好啦~所以說,建議你一定要養成良好的 script 撰寫習慣,在每個 script 的檔案開始處記錄好:

script 的功能;

script 的版本資訊;

script 的作者與聯絡方式;

script 的版權宣告方式;

script 的 History (歷史紀錄);

script 內較特殊的指令,使用“絕對路徑”的方式來下達;

script 執行時需要的環境變數預先宣告與設定。

除了記錄這些資訊之外,在較為特殊的程式碼部分,個人建議務必要加上註解說明,可以幫助你非常非常多! 此外,程式碼的撰寫最好使用巢狀方式,在包覆的內部程式碼最好能以[tab] 按鍵的空格向後推, 這樣你的程式碼會顯的非常的漂亮與有條理!在查閱與 debug 上較為輕鬆愉快喔! 另外,使用撰寫 script 的工具最好使用 vim 而不是 vi ,因為 vim 會有額外的語法檢驗機制,能夠在第一階段撰寫時就發現語法方面的問題喔!

 

二、簡單的 shell script 練習

在第一支 shell script 撰寫完畢之後,相信你應該具有基本的撰寫功力了。接下來,在開始更深入的程式概念之前,我們先來玩一些簡單的小范例好了。 下面的範例中,達成結果的方式相當的多,建議你先自行撰寫看看,寫完之後再與鳥哥寫的內容比對, 這樣才能更加深概念喔!好!不囉唆,我們就一個一個來玩吧!

1,簡單範例

下面的範例在很多的指令碼程式中都會用到,而下面的範例又都很簡單!值得參考看看喔!

對談式指令碼:變數內容由使用者決定

很多時候我們需要使用者輸入一些內容,好讓程式可以順利執行。 簡單的來說,大家應該都有安裝過軟體的經驗,安裝的時候,他不是會問你“要安裝到那個目錄去”嗎? 那個讓使用者輸入資料的動作,就是讓使用者輸入變數內容啦。

你應該還記得在十章 bash 的時候,我們有學到一個 read 指令吧?現在,請你以 read 指令的用途,撰寫一個 script ,他可以讓使用者輸入:1. first name 與 2. last name, 最後並且在螢幕上顯示:“Your full name is: ”的內容:

[[email protected] bin]$ vim showname.sh

#!/bin/bash

# Program:

# User inputs his first name and last name. Program shows his full name.

# History:

# 2015/07/16 VBird First release

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin

export PATH

read -p "Please input your first name: " firstname # 提示使用者輸入

read -p "Please input your last name: " lastname # 提示使用者輸入

echo -e "\nYour full name is: ${firstname} ${lastname}" # 結果由螢幕輸出

 

將上面這個 showname.sh 執行一下,你就能夠發現使用者自己輸入的變數可以讓程式所取用,並且將他顯示到螢幕上! 接下來,如果想要製作一個每次執行都會依據不同的日期而變化結果的指令碼呢?(shell程式設計應該還是屬於函式式的程式設計,在處理linux服務時比較有用)

隨日期變化:利用 date 進行檔案的建立

想像一個狀況,假設我的伺服器內有資料庫,資料庫每天的資料都不太一樣,因此當我備份時,希望將每天的資料都備份成不同的檔名, 這樣才能夠讓舊的資料也能夠儲存下來不被覆蓋。哇!不同檔名呢!這真困擾啊?難道要我每天去修改 script ?

不需要啊!考慮每天的“日期”並不相同,所以我可以將檔名取成類似: backup.2015-07-16.data , 不就可以每天一個不同檔名了嗎?呵呵!確實如此。那個 2015-07-16 怎麼來的?那就是重點啦!接下來出個相關的例子: 假設我想要建立三個空的檔案 (通過 touch),檔名最開頭由使用者輸入決定,假設使用者輸入 filename 好了,那今天的日期是2015/07/16 , 我想要以前天、昨天、今天的日期來建立這些檔案,亦即 filename_20150714,filename_20150715, filename_20150716 ,該如何是好?(所以日誌基本上都是檔名+月份的格式,在linux中檔名都賊長)

[[email protected] bin]$ vim create_3_filename.sh

#!/bin/bash

# Program:

# Program creates three files, which named by user's input and date command.

# History:

# 2015/07/16 VBird First release

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin

export PATH

# 1\. 讓使用者輸入檔名稱,並取得 fileuser 這個變數;

echo -e "I will use 'touch' command to create 3 files." # 純粹顯示資訊

read -p "Please input your filename: " fileuser # 提示使用者輸入

# 2\. 為了避免使用者隨意按 Enter ,利用[變數功能](../Text/index.html#variable_other_re)分析檔名是否有設定?

filename=${fileuser:-"filename"} # 開始判斷有否配置檔名

# 3\. 開始利用 date 指令來取得所需要的檔名了;

date1=$(date --date='2 days ago' +%Y%m%d) # 前兩天的日期

date2=$(date --date='1 days ago' +%Y%m%d) # 前一天的日期

date3=$(date +%Y%m%d) # 今天的日期

file1=${filename}${date1} # 下面三行在配置檔名

file2=${filename}${date2}

file3=${filename}${date3}

# 4\. 將檔名建立吧!

touch "${file1}" # 下面三行在建立檔案

touch "${file2}"

touch "${file3}

 

上面的範例鳥哥使用了很多在第十章介紹過的概念: 包括小指令“ $(command) ”的取得訊息、變數的設定功能、變數的累加以及利用 touch 指令輔助! 如果你開始執行這個create_3_filename.sh 之後,你可以進行兩次執行:一次直接按 [Enter] 來查閱檔名是啥?一次可以輸入一些字元,這樣可以判斷你的指令碼是否設計正確喔!

 

數值運算:簡單的加減乘除

各位看官應該還記得,我們可以使用 declare 來定義變數的型別吧? 當變數定義成為整數後才能夠進行加減運算啊!此外,我們也可以利用“ $((計算式)) ”來進行數值運算的。 可惜的是, bash shell 裡頭預設僅支援到整數的資料而已。OK!那我們來玩玩看,如果我們要使用者輸入兩個變數, 然後將兩個變數的內容相乘,最後輸出相乘的結果,那可以怎麼做?

[[email protected] bin]$ vim multiplying.sh

#!/bin/bash

# Program:

# User inputs 2 integer numbers; program will cross these two numbers.

# History:

# 2015/07/16 VBird First release

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin

export PATH

echo -e "You SHOULD input 2 numbers, I will multiplying them! \n"

read -p "first number: " firstnu

read -p "second number: " secnu

total=$((${firstnu}*${secnu}))

echo -e "\nThe result of ${firstnu} x ${secnu} is ==> ${total}"

 

在數值的運算上,我們可以使用“ declare -i total=${firstnu}*${secnu} ” 也可以使用上面的方式來進行!基本上,鳥哥比較建議使用這樣的方式來進行運算:

var=$((運算內容))

不但容易記憶,而且也比較方便的多,因為兩個小括號內可以加上空白字元喔! 未來你可以使用這種方式來計算的呀!至於數值運算上的處理,則有:“ +, -, , /, % ”等等。 那個 % 是取餘數啦~舉例來說, 13 對 3 取餘數,結果是 13=43+1,所以餘數是 1 啊!就是:

[[email protected] bin]$ echo $(( 13 % 3 ))

1

 

這樣瞭解了吧?另外,如果你想要計算含有小數點的資料時,其實可以通過 bc 這個指令的協助喔! 例如可以這樣做:

[[email protected] bin]$ echo "123.123*55.9" | bc

6882.575

 

瞭解了 bc 的妙用之後,來讓我們測試一下如何計算 pi 這個東西呢?

數值運算:通過 bc 計算 pi

其實計算 pi 時,小數點以下位數可以無限制的延伸下去!而 bc 有提供一個運算 pi 的函式,只是想要使用該函式必須要使用 bc -l 來呼叫才行。 也因為這個小數點以下位數可以無線延伸運算的特性存在,所以我們可以通過下面這隻小指令碼來讓使用者輸入一個“小數點為數值”,以讓 pi 能夠更準確!

[[email protected] bin]$ vim cal_pi.sh

#!/bin/bash

# Program:

# User input a scale number to calculate pi number.

# History:

# 2015/07/16 VBird First release

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin

export PATH

echo -e "This program will calculate pi value. \n"

echo -e "You should input a float number to calculate pi value.\n"

read -p "The scale number (10~10000) ? " checking

num=${checking:-"10"} # 開始判斷有否有輸入數值

echo -e "Starting calcuate pi value. Be patient."

time echo "scale=${num}; 4*a(1)" | bc -lq

 

上述資料中,那個 4*a(1) 是 bc 主動提供的一個計算 pi 的函式,至於 scale 就是要 bc 計算幾個小數點下位數的意思。當 scale 的數值越大, 代表 pi 要被計算的越精確,當然用掉的時間就會越多!因此,你可以嘗試輸入不同的數值看看!不過,最好是不要超過 5000 啦!因為會算很久! 如果你要讓你的 CPU 隨時保持在高負載,這個程式算下去你就會知道有多操CPU 囉! ^_^

Tips 鳥哥的實驗室中,為了要確認虛擬機器的效率問題,所以很多時候需要保持虛擬機器在忙碌的狀態~鳥哥的學生就是丟這隻程式進去系統跑! 但是將 scale 調高一些,那計算就得要花比較多時間~用以達到我們需要 CPU 忙碌的狀態喔!

 

2,script 的執行方式差異 (source, sh script, ./script)

不同的 script 執行方式會造成不一樣的結果喔!尤其影響 bash 的環境很大呢!指令碼的執行方式除了前面小節談到的方式之外,還可以利用 source 或小數點 (.) 來執行喔!那麼這種執行方式有何不同呢?當然是不同的啦!讓我們來說說!(這一點我也一直不清楚,學習了)

利用直接執行的方式來執行 script

當使用前一小節提到的直接指令下達 (不論是絕對路徑/相對路徑還是 ${PATH} 內),或者是利用 bash (或 sh) 來下達指令碼時, 該 script 都會使用一個新的 bash 環境來執行指令碼內的指令!也就是說,使用這種執行方式時, 其實 script 是在子程式的 bash 內執行的!我們在第十章 BASH 內談到 export 的功能時,曾經就父程式/子程式談過一些概念性的問題, 重點在於:“當子程式完成後,在子程式內的各項變數或動作將會結束而不會傳回到父程式中”!這是什麼意思呢?

我們舉剛剛提到過的 showname.sh 這個指令碼來說明好了,這個指令碼可以讓使用者自行設定兩個變數,分別是 firstname 與 lastname,想一想,如果你直接執行該指令時,該指令幫你設定的 firstname 會不會生效?看一下下面的執行結果:

[[email protected] bin]$ echo ${firstname} ${lastname}

<==確認了,這兩個變數並不存在喔!

[[email protected] bin]$ sh showname.sh

Please input your first name: VBird <==這個名字是鳥哥自己輸入的

Please input your last name: Tsai

Your full name is: VBird Tsai <==看吧!在 script 執行中,這兩個變數有生效

[[email protected] bin]$ echo ${firstname} ${lastname}

<==事實上,這兩個變數在父程式的 bash 中還是不存在的!

 

上面的結果你應該會覺得很奇怪,怎麼我已經利用 showname.sh 設定好的變數竟然在 bash環境下面無效!怎麼回事呢? 如果將程式相關性繪製成圖的話,我們以下圖來說明。當你使用直接執行的方法來處理時,系統會給予一支新的 bash 讓我們來執行 showname.sh 裡面的指令,因此你的 firstname, lastname 等變數其實是在下圖中的子程式 bash 內執行的。 當showname.sh 執行完畢後,子程式 bash 內的所有資料便被移除,因此上表的練習中,在父程式下面 echo ${firstname} 時, 就看不到任何東西了!這樣可以理解嗎?

利用 source 來執行指令碼:在父程式中執行

如果你使用 source 來執行指令那就不一樣了!同樣的指令碼我們來執行看看:

[[email protected] bin]$ source showname.sh

Please input your first name: VBird

Please input your last name: Tsai

Your full name is: VBird Tsai

[[email protected] bin]$ echo ${firstname} ${lastname}

VBird Tsai <==嘿嘿!有資料產生喔!

 

竟然生效了!沒錯啊!因為 source 對 script 的執行方式可以使用下面的圖示來說明!showname.sh 會在父程式中執行的,因此各項動作都會在原本的 bash 內生效!這也是為啥你不登出系統而要讓某些寫入 ~/.bashrc 的設定生效時,需要使用“ source ~/.bashrc ”而不能使用“ bash ~/.bashrc ”是一樣的啊!

./執行script

./會用你指令碼中第一行的那個#!XXX的shell來執行語句,而sh 則是用sh來執行語句還有一種可能就是./確定了檔案路徑,而sh 一定要在scipt所在目錄