一、shell函數
我們的報告當前執行如下步驟,即可生成HTML文檔。
- 打開頁面
- 打開頁面標題
- 設置頁面標題
- 關閉頁面標題
- 打開頁面主體
- 輸出頁面主體
- 輸出時間戳
- 關閉頁面主體
- 關閉頁面
爲了下一階段的開發,我們將在步驟7和步驟8之間額外添加一些任務,如下所示。
- 系統正常運行時間和負載。這是自上次關機或重啓之後系統的運行時間,在幾個時間間隔內,當前運行在處理器上的平均任務量。
- 磁盤空間。系統存儲空間的使用情況。
- 用戶空間。每個用戶所使用的存儲空間。
在這些任務中,如果每個任務都有一條對應的命令,我們可以直接通過命令替換的方式,將它們添加到腳本中。
#!/bin/bash
#Program to output a system information page
TITLE="System Information Report For $HOSTNAME"
CURRENT_TIME=$(date +"%x %r %Z")
TIME_STAMP="Generated $CURRENT_TIME,by #USER"
cat << _EOF_
<HTML>
<HEAD>
<TITLE>$TITLE</TITLE>
</HEAD>
<BODY>
<H1>$TITLE</H1>
<P>$TIME_STAMP</P>
$(report_uptime)
$(report_disk_space)
$(report_home_space)
</BODY>
</HTML>
_EOF_
我們可以通過兩種方式創建這些額外的命令。我們可以編寫3個獨立的腳本,並把它們放置到環境變量PATH所列出的目錄中,或者是我們可以將腳本作爲shell函數嵌入到程序i中。shell函數是位於其他腳本中的迷你腳本,可以用作自主程序(autonomous program)。shell函數由兩種語法形式。第一種如下所示:
function name {
commands
return
}
其中name是指示這個函數的名稱,commands是這個函數中的一系列命令。第二種看起來如下所示:
name() {
commands
return
}
這兩種格式等價,並且可以交替使用。來看下面這個腳本,它演示了shell函數的使用。
#!/bin/bash
#shell function demo
function funct {
echo "Step 2"
return
}
#Main program starts here
echo "Step 1"
funct
echo "Step 3"
當shell讀取腳本的時候,它會跳過1~11行,這些行包含的是註釋和函數的定義。執行從帶有echo
命令的第12行開始。第13行調用了shell函數funct,shell會執行這個函數,而且其執行方式與執行其他命令時相同。程序控制權然後移動到第6行,執行第二個echo
命令,隨後再執行第7行。這個return命令終止函數的執行,然後將控制權漢給函數調用後面的代碼(第14行),然後執行最後一個echo
命令。
注意:爲了讓函數調用被識別爲shell函數,而不是被解釋爲外部程序的名字,shell函數的定義在腳本中的位置必須在它被調用的前面。
我們在腳本中添加上最小的shell函數定義:
#!/bin/bash
#Program to output a system information page
TITLE="System Information Report For $HOSTNAME"
CURRENT_TIME=$(date +"%x %r %Z")
TIME_STAMP="Generated $CURRENT_TIME,by #USER"
report_uptime () {
return
}
report_disk_space () {
return
}
report_home_space () {
return
}
cat << _EOF_
<HTML>
<HEAD>
<TITLE>$TITLE</TITLE>
</HEAD>
<BODY>
<H1>$TITLE</H1>
<P>$TIME_STAMP</P>
$(report_uptime)
$(report_disk_space)
$(report_home_space)
</BODY>
</HTML>
_EOF_
shell函數的命令規則和變量相同。一個函數必須至少包含一條命令。return命令(可選的)可以滿足該要求。
二、局部變量
在目前我們所編寫的腳本中,所有的變量(包括常量)都是全局變量。全局變量在整個程序期間會一直存在。在很多情況下,這很不錯。但是有時候,它會讓shell函數的使用變得複雜。在shell函數中,經常需要的是局部變量。局部變量僅僅在定義他們的shell函數中有效,一旦shell函數終止,它們就不再存在。
局部變量可以讓程序員使用已經存在的變量名稱,無論是腳本中的全局變量,還是其他shell函數中的變量,而不用考慮潛在的命名衝突。
下面是一個顯示如何定義和使用局部變量的腳本示例。
#!/bin/bash
#local-vars: script to demonstrate local variables
foo=0 #global variable foo
funct_1 () {
local foo # variable foo local to funct_1
foo=1
echo "funct_1: foo=$foo"
}
funct_2 () {
local foo # variable foo local to funct_2
foo=2
echo "funct_2: foo=$foo"
}
echo "global: foo = $foo"
funct_1
echo "global: foo = $foo"
funct_2
echo "global: foo = $foo"
可以看到,局部變量是通過在變量名前面添加單詞local來定義的。這樣,就創建並同時定義了一個shell函數中的局部變量。一旦出了這個shell函數,這個局部變量將不再存在。當運行腳步時,結果如下所示:
[wangjichuan@Jarvis ~]$ local-vars
global: foo = 0
funct_1: foo=1
global: foo = 0
funct_2: foo=2
global: foo = 0
可以看到,在兩個shell函數中對局部變量foo的賦值,不影響shell函數以外定義的變量foo的值。
這個特性可以讓我們編寫的shell函數相互獨立,而且也獨立於它們所在的腳本。這非常有用,因爲它有助於阻止程序中各個部分的相互干擾。該特性也可以讓我們編寫出可移植的shell函數。也就是說,我們可以根據需要將某一腳本中的shell函數剪切下來,然後粘貼到另外一個腳本中。
三、保持腳本的運行
在開發程序時,讓程序保持可運行的狀態會非常有用。通過這種方式,並經常測試,就可以在開發過程的早期檢測到錯誤。這也會讓問題的調試變得容易。例如,如果我們運行某個程序,並對它做了細微的改動,然後再次運行該程序,此時發現了問題。這可能時最近的改動導致的。通過添加空函數(程序員將其成爲stub),我們可以在早期驗證程序的邏輯流程。當構建一個stub時,最好時包含一些能夠爲程序員提供反饋信息的東西,這些反饋信息可以顯示正在執行的邏輯流程。
report_uptime () {
cat <<- _EOF_
<H2>System Uptime</H2>
<PRE>$(UPTIME)</PRE>
_EOF_
return
}
該函數的代碼直接了當。我們使用了一個here文檔來輸出一個標題,並輸出uptime命令的結果。命令結果使用
標籤圍起來,其目的是保留命令的格式。report_disk_space函數也類似。
report_disk_space () {
cat <<- _EOF_
<H2>Disk Space Utilization</H2>
<PRE>$(df -h)</PRE>
_EOF_
return
}
這個函數使用了df -h
這個命令來檢查磁盤的空間。隨後,我們來構建report_home_space函數。
report_home_space () {
cat <<- _EOF_
<H2>Home Space Utilization</H2>
<PRE>$(du -sh /home/*)</PRE>
_EOF_
return
}
我們使用帶-sh選項的du
命令來執行這個任務。這個並不是問題的完整解決方案。雖然可以在某些操作系統中(例如Ubuntu)運行,但是在其他系統中則無效。這是因爲很多系統設置了主目錄的權限,以防止被其他用戶讀取,這種安全措施也很合理。在這些操作系統中,只有在超級用戶運行我們的腳本時,我們編寫的這個report_home_space函數纔會執行。一個更好的解決方案是讓腳本根據用戶的權限調整自己的行爲。
你的.bashrc
文件中的shell函數
shell函數可以很好的取代別名,並且實際上也是創建個人使用的小命令的首選方法。別名非常侷限於命令的種類和它們所支持的shell特性,而shell特性則允許任何可以編寫爲腳本的東西。