## 12.2 簡單的 shell script 練習
在第一支 shell script 撰寫完畢之后,相信你應該具有基本的撰寫功力了。接下來,在開始更深入的程序概念之前,我們先來玩一些簡單的小范例好了。 下面的范例中,達成結果的方式相當的多,建議你先自行撰寫看看,寫完之后再與鳥哥寫的內容比對, 這樣才能更加深概念喔!好!不啰唆,我們就一個一個來玩吧!
### 12.2.1 簡單范例
下面的范例在很多的腳本程序中都會用到,而下面的范例又都很簡單!值得參考看看喔!
* 對談式腳本:變量內容由使用者決定
很多時候我們需要使用者輸入一些內容,好讓程序可以順利運行。 簡單的來說,大家應該都有安裝過軟件的經驗,安裝的時候,他不是會問你“要安裝到那個目錄去”嗎? 那個讓使用者輸入數據的動作,就是讓使用者輸入變量內容啦。
你應該還記得在[十章 bash](../Text/index.html) 的時候,我們有學到一個 [read](../Text/index.html#read) 指令吧?現在,請你以 read 指令的用途,撰寫一個 script ,他可以讓使用者輸入:1\. first name 與 2\. last name, 最后并且在屏幕上顯示:“Your full name is: ”的內容:
```
[dmtsai@study 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 執行一下,你就能夠發現使用者自己輸入的變量可以讓程序所取用,并且將他顯示到屏幕上! 接下來,如果想要制作一個每次執行都會依據不同的日期而變化結果的腳本呢?
* 隨日期變化:利用 date 進行文件的創建
想像一個狀況,假設我的服務器內有數據庫,數據庫每天的數據都不太一樣,因此當我備份時,希望將每天的數據都備份成不同的文件名, 這樣才能夠讓舊的數據也能夠保存下來不被覆蓋。哇!不同文件名呢!這真困擾啊?難道要我每天去修改 script ?
不需要啊!考慮每天的“日期”并不相同,所以我可以將文件名取成類似: backup.2015-07-16.data , 不就可以每天一個不同文件名了嗎?呵呵!確實如此。那個 2015-07-16 怎么來的?那就是重點啦!接下來出個相關的例子: 假設我想要創建三個空的文件 (通過 [touch](../Text/index.html#touch)) ,文件名最開頭由使用者輸入決定,假設使用者輸入 filename 好了,那今天的日期是 2015/07/16 , 我想要以前天、昨天、今天的日期來創建這些文件,亦即 filename_20150714, filename_20150715, filename_20150716 ,該如何是好?
```
[dmtsai@study 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}"
```
上面的范例鳥哥使用了很多在[第十章](../Text/index.html)介紹過的概念: 包括小指令“ $(command) ”的取得訊息、變量的設置功能、變量的累加以及利用 touch 指令輔助! 如果你開始執行這個 create_3_filename.sh 之后,你可以進行兩次執行:一次直接按 [Enter] 來查閱文件名是啥? 一次可以輸入一些字符,這樣可以判斷你的腳本是否設計正確喔!
* 數值運算:簡單的加減乘除
各位看官應該還記得,我們可以使用 [declare](../Text/index.html#declare) 來定義變量的類型吧? 當變量定義成為整數后才能夠進行加減運算啊!此外,我們也可以利用“ $((計算式)) ”來進行數值運算的。 可惜的是, bash shell 里頭默認僅支持到整數的數據而已。OK!那我們來玩玩看,如果我們要使用者輸入兩個變量, 然后將兩個變量的內容相乘,最后輸出相乘的結果,那可以怎么做?
```
[dmtsai@study 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=4*3+1,所以余數是 1 啊!就是:
```
[dmtsai@study bin]$ echo $(( 13 % 3 ))
1
```
這樣了解了吧?另外,如果你想要計算含有小數點的數據時,其實可以通過 [bc](../Text/index.html#bc) 這個指令的協助喔! 例如可以這樣做:
```
[dmtsai@study bin]$ echo "123.123*55.9" | bc
6882.575
```
了解了 bc 的妙用之后,來讓我們測試一下如何計算 pi 這個東西呢?
* 數值運算:通過 bc 計算 pi
其實計算 pi 時,小數點以下位數可以無限制的延伸下去!而 bc 有提供一個運算 pi 的函數,只是想要使用該函數必須要使用 bc -l 來調用才行。 也因為這個小數點以下位數可以無線延伸運算的特性存在,所以我們可以通過下面這只小腳本來讓使用者輸入一個“小數點為數值”, 以讓 pi 能夠更準確!
```
[dmtsai@study 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 忙碌的狀態喔!
### 12.2.2 script 的執行方式差異 (source, sh script, ./script)
不同的 script 執行方式會造成不一樣的結果喔!尤其影響 bash 的環境很大呢!腳本的執行方式除了[前面小節談到的方式](../Text/index.html#script_run)之外,還可以利用 [source](../Text/index.html#source) 或小數點 (.) 來執行喔!那么這種執行方式有何不同呢?當然是不同的啦!讓我們來說說!
* 利用直接執行的方式來執行 script
當使用前一小節提到的直接指令下達 (不論是絕對路徑/相對路徑還是 ${PATH} 內),或者是利用 bash (或 sh) 來下達腳本時, 該 script 都會使用一個新的 bash 環境來執行腳本內的指令!也就是說,使用這種執行方式時, 其實 script 是在子程序的 bash 內執行的!我們在[第十章 BASH](../Text/index.html) 內談到 [export](../Text/index.html#export) 的功能時,曾經就父程序/子程序談過一些概念性的問題, 重點在于:“當子程序完成后,在子程序內的各項變量或動作將會結束而不會傳回到父程序中”! 這是什么意思呢?
我們舉剛剛提到過的 showname.sh 這個腳本來說明好了,這個腳本可以讓使用者自行設置兩個變量,分別是 firstname 與 lastname,想一想,如果你直接執行該指令時,該指令幫你設置的 firstname 會不會生效?看一下下面的執行結果:
```
[dmtsai@study bin]$ echo ${firstname} ${lastname}
<==確認了,這兩個變量并不存在喔!
[dmtsai@study bin]$ sh showname.sh
Please input your first name: VBird <==這個名字是鳥哥自己輸入的
Please input your last name: Tsai
Your full name is: VBird Tsai <==看吧!在 script 運行中,這兩個變量有生效
[dmtsai@study bin]$ echo ${firstname} ${lastname}
<==事實上,這兩個變量在父程序的 bash 中還是不存在的!
```
上面的結果你應該會覺得很奇怪,怎么我已經利用 showname.sh 設置好的變量竟然在 bash 環境下面無效!怎么回事呢? 如果將程序相關性繪制成圖的話,我們以下圖來說明。當你使用直接執行的方法來處理時,系統會給予一支新的 bash 讓我們來執行 showname.sh 里面的指令,因此你的 firstname, lastname 等變量其實是在下圖中的子程序 bash 內執行的。 當 showname.sh 執行完畢后,子程序 bash 內的所有數據便被移除,因此上表的練習中,在父程序下面 echo ${firstname} 時, 就看不到任何東西了!這樣可以理解嗎?
圖12.2.1、showname.sh 在子程序當中運行的示意圖
* 利用 source 來執行腳本:在父程序中執行
如果你使用 source 來執行指令那就不一樣了!同樣的腳本我們來執行看看:
```
[dmtsai@study bin]$ source showname.sh
Please input your first name: VBird
Please input your last name: Tsai
Your full name is: VBird Tsai
[dmtsai@study bin]$ echo ${firstname} ${lastname}
VBird Tsai <==嘿嘿!有數據產生喔!
```
竟然生效了!沒錯啊!因為 source 對 script 的執行方式可以使用下面的圖示來說明! showname.sh 會在父程序中執行的,因此各項動作都會在原本的 bash 內生效!這也是為啥你不登出系統而要讓某些寫入 ~/.bashrc 的設置生效時,需要使用“ source ~/.bashrc ”而不能使用“ bash ~/.bashrc ”是一樣的啊!
圖12.2.2、showname.sh 在父程序當中運行的示意圖
- 鳥哥的Linux私房菜:基礎學習篇 第四版
- 目錄及概述
- 第零章、計算機概論
- 0.1 電腦:輔助人腦的好工具
- 0.2 個人電腦架構與相關設備元件
- 0.3 數據表示方式
- 0.4 軟件程序運行
- 0.5 重點回顧
- 0.6 本章習題
- 0.7 參考資料與延伸閱讀
- 第一章、Linux是什么與如何學習
- 1.1 Linux是什么
- 1.2 Torvalds的Linux發展
- 1.3 Linux當前應用的角色
- 1.4 Linux 該如何學習
- 1.5 重點回顧
- 1.6 本章習題
- 1.7 參考資料與延伸閱讀
- 第二章、主機規劃與磁盤分區
- 2.1 Linux與硬件的搭配
- 2.2 磁盤分區
- 2.3 安裝Linux前的規劃
- 2.4 重點回顧
- 2.5 本章習題
- 2.6 參考資料與延伸閱讀
- 第三章、安裝 CentOS7.x
- 3.1 本練習機的規劃--尤其是分區參數
- 3.2 開始安裝CentOS 7
- 3.3 多重開機安裝流程與管理(Option)
- 3.4 重點回顧
- 3.5 本章習題
- 3.6 參考資料與延伸閱讀
- 第四章、首次登陸與線上求助
- 4.1 首次登陸系統
- 4.2 文字模式下指令的下達
- 4.3 Linux系統的線上求助man page與info page
- 4.4 超簡單文書編輯器: nano
- 4.5 正確的關機方法
- 4.6 重點回顧
- 4.7 本章習題
- 4.8 參考資料與延伸閱讀
- 第五章、Linux 的文件權限與目錄配置
- 5.1 使用者與群組
- 5.2 Linux 文件權限概念
- 5.3 Linux目錄配置
- 5.4 重點回顧
- 5.5 本章練習
- 5.6 參考資料與延伸閱讀
- 第六章、Linux 文件與目錄管理
- 6.1 目錄與路徑
- 6.2 文件與目錄管理
- 6.3 文件內容查閱
- 6.4 文件與目錄的默認權限與隱藏權限
- 6.5 指令與文件的搜尋
- 6.6 極重要的復習!權限與指令間的關系
- 6.7 重點回顧
- 6.8 本章習題:
- 6.9 參考資料與延伸閱讀
- 第七章、Linux 磁盤與文件系統管理
- 7.1 認識 Linux 文件系統
- 7.2 文件系統的簡單操作
- 7.3 磁盤的分區、格式化、檢驗與掛載
- 7.4 設置開機掛載
- 7.5 內存交換空間(swap)之創建
- 7.6 文件系統的特殊觀察與操作
- 7.7 重點回顧
- 7.8 本章習題 - 第一題一定要做
- 7.9 參考資料與延伸閱讀
- 第八章、文件與文件系統的壓縮,打包與備份
- 8.1 壓縮文件的用途與技術
- 8.2 Linux 系統常見的壓縮指令
- 8.3 打包指令: tar
- 8.4 XFS 文件系統的備份與還原
- 8.5 光盤寫入工具
- 8.6 其他常見的壓縮與備份工具
- 8.7 重點回顧
- 8.8 本章習題
- 8.9 參考資料與延伸閱讀
- 第九章、vim 程序編輯器
- 9.1 vi 與 vim
- 9.2 vi 的使用
- 9.3 vim 的額外功能
- 9.4 其他 vim 使用注意事項
- 9.5 重點回顧
- 9.6 本章練習
- 9.7 參考資料與延伸閱讀
- 第十章、認識與學習BASH
- 10.1 認識 BASH 這個 Shell
- 10.2 Shell 的變量功能
- 10.3 命令別名與歷史命令
- 10.4 Bash Shell 的操作環境:
- 10.5 數據流重導向
- 10.6 管線命令 (pipe)
- 10.7 重點回顧
- 10.8 本章習題
- 10.9 參考資料與延伸閱讀
- 第十一章、正則表達式與文件格式化處理
- 11.1 開始之前:什么是正則表達式
- 11.2 基礎正則表達式
- 11.3 延伸正則表達式
- 11.4 文件的格式化與相關處理
- 11.5 重點回顧
- 11.6 本章習題
- 11.7 參考資料與延伸閱讀
- 第十二章、學習 Shell Scripts
- 12.1 什么是 Shell scripts
- 12.2 簡單的 shell script 練習
- 12.3 善用判斷式
- 12.4 條件判斷式
- 12.5 循環 (loop)
- 12.6 shell script 的追蹤與 debug
- 12.7 重點回顧
- 12.8 本章習題
- 第十三章、Linux 帳號管理與 ACL 權限設置
- 13.1 Linux 的帳號與群組
- 13.2 帳號管理
- 13.3 主機的細部權限規劃:ACL 的使用
- 13.4 使用者身份切換
- 13.5 使用者的特殊 shell 與 PAM 模塊
- 13.6 Linux 主機上的使用者訊息傳遞
- 13.7 CentOS 7 環境下大量創建帳號的方法
- 13.8 重點回顧
- 13.9 本章習題
- 13.10 參考資料與延伸閱讀
- 第十四章、磁盤配額(Quota)與進階文件系統管理
- 14.1 磁盤配額 (Quota) 的應用與實作
- 14.2 軟件磁盤陣列 (Software RAID)
- 14.3 邏輯卷軸管理員 (Logical Volume Manager)
- 14.4 重點回顧
- 14.5 本章習題
- 14.6 參考資料與延伸閱讀
- 第十五章、例行性工作調度(crontab)
- 15.1 什么是例行性工作調度
- 15.2 僅執行一次的工作調度
- 15.3 循環執行的例行性工作調度
- 15.4 可喚醒停機期間的工作任務
- 15.5 重點回顧
- 15.6 本章習題
- 第十六章、程序管理與 SELinux 初探
- 16.1 什么是程序 (process)
- 16.2 工作管理 (job control)
- 16.3 程序管理
- 16.4 特殊文件與程序
- 16.5 SELinux 初探
- 16.6 重點回顧
- 16.7 本章習題
- 16.8 參考資料與延伸閱讀
- 第十七章、認識系統服務 (daemons)
- 17.1 什么是 daemon 與服務 (service)
- 17.2 通過 systemctl 管理服務
- 17.3 systemctl 針對 service 類型的配置文件
- 17.4 systemctl 針對 timer 的配置文件
- 17.5 CentOS 7.x 默認啟動的服務簡易說明
- 17.6 重點回顧
- 17.7 本章習題
- 17.8 參考資料與延伸閱讀
- 第十八章、認識與分析登錄文件
- 18.1 什么是登錄文件
- 18.2 rsyslog.service :記錄登錄文件的服務
- 18.3 登錄文件的輪替(logrotate)
- 18.4 systemd-journald.service 簡介
- 18.5 分析登錄文件
- 18.6 重點回顧
- 18.7 本章習題
- 18.8 參考資料與延伸閱讀
- 第十九章、開機流程、模塊管理與 Loader
- 19.1 Linux 的開機流程分析
- 19.2 核心與核心模塊
- 19.3 Boot Loader: Grub2
- 19.4 開機過程的問題解決
- 19.5 重點回顧
- 19.6 本章習題
- 19.7 參考資料與延伸閱讀
- 第二十章、基礎系統設置與備份策略
- 20.1 系統基本設置
- 20.2 服務器硬件數據的收集
- 20.3 備份要點
- 20.4 備份的種類、頻率與工具的選擇
- 20.5 鳥哥的備份策略
- 20.6 災難復原的考慮
- 20.7 重點回顧
- 20.8 本章習題
- 20.9 參考資料與延伸閱讀
- 第二十一章、軟件安裝:源代碼與 Tarball
- 20.1 開放源碼的軟件安裝與升級簡介
- 21.2 使用傳統程序語言進行編譯的簡單范例
- 21.3 用 make 進行宏編譯
- 21.4 Tarball 的管理與建議
- 21.5 函數庫管理
- 21.6 檢驗軟件正確性
- 21.7 重點回顧
- 21.8 本章習題
- 21.9 參考資料與延伸閱讀
- 第二十二章、軟件安裝 RPM, SRPM 與 YUM
- 22.1 軟件管理員簡介
- 22.2 RPM 軟件管理程序: rpm
- 22.3 YUM 線上升級機制
- 22.4 SRPM 的使用 : rpmbuild (Optional)
- 22.5 重點回顧
- 22.6 本章習題
- 22.7 參考資料與延伸閱讀
- 第二十三章、X Window 設置介紹
- 23.1 什么是 X Window System
- 23.2 X Server 配置文件解析與設置
- 23.3 顯卡驅動程序安裝范例
- 23.4 重點回顧
- 23.5 本章習題
- 23.6 參考資料與延伸閱讀
- 第二十四章、Linux 核心編譯與管理
- 24.1 編譯前的任務:認識核心與取得核心源代碼
- 24.2 核心編譯的前處理與核心功能選擇
- 24.3 核心的編譯與安裝
- 24.4 額外(單一)核心模塊編譯
- 24.5 以最新核心版本編譯 CentOS 7.x 的核心
- 24.6 重點回顧
- 24.7 本章習題
- 24.8 參考資料與延伸閱讀