為使shell編程更有效,系統提供了一些shell變數。shell變數可以儲存諸如路徑名、檔案名稱或者一個數字這樣的變數名。shell將其中任何設定都看做文本字串。
有兩種變數,本地和環境。嚴格地說可以有4種,但其餘兩種是唯讀,可以認為是特殊變數,它用於向shell指令碼傳遞參數。
1 什麼是shell變數
按照定義來說,變數是存放裝置中的一些可讀寫的儲存單元.變數可以定製使用者本身的工作環境。使用變數可以儲存有用資訊,使系統獲知使用者相關設定。變數也用 於儲存暫時資訊。例如:一變數為EDITOR,系統中有許多編輯工具,但哪一個適用於系統呢?將此編輯器名稱賦給EDITOR,這樣,在使用cron或其 他需要編輯器的應用
時,這就是你將一直使用的EDITOR取值,並將之用作預設編輯器。舉例來說,登入的審核系統需要編輯。在菜單中選擇此選項時,應用查詢EDITOR變數值,其值為vi。系統知道可使用此編輯器(用命令echo $EDITOR看看自己的?)。另一個例子需要登入資料庫系統,鍵入下列命令:
$ isql -Udavet -Pabcd -Smethsys
這裡- S為正在已連線的服務器名稱。有一變數DSQUERY儲存伺服器名稱值。設定伺服器名稱值到DSQUERY變數,這樣如果登入時不使用-S提供伺服器名稱, 應用將查詢DSQUERY變數,並使用其取值作為伺服器名稱。需要做的全部工作就是鍵入下列命令:
$ isql -Udavet -Pabcd
工作方式同上例。
2 本地變數
本 地變數在使用者現在的shell生命期的指令碼中使用。例如,本地變數file-name取值為loop.doc,這個值只在使用者當前shell生命期有意 義。如果在shell中啟動另一個進程或退出,此值將無效。這個方法的優點就是使用者不能對其他的shell或進程設定此變數有效。使用變數時,如果用花括弧將之括起來,可以防止shell誤解變數值,儘管不必一定要這樣做,但這確實可用。要設定一本地變數,格式為:
$ variable-name=value 或 $ {variable-name=value}注意,等號兩邊可以有空格。如果取值包含空格,必須用雙引號括起來。shell變數可以用大小寫字母。變數設定時的不同模式:
Variable-name=value 設定實際值到variable-name
Variable-name+value 如果設定了variable-name,則重設其值
Variable-name:?value 如果未設定variable-name,顯示未定義使用者錯誤資訊
Variable-name?value 如果未設定variable-name,顯示系統錯誤資訊
Variable-name:=value 如果未設定variable-name,設定其值
Variable-name:-value 同上,但是取值並不設定到variable-name,可以被替換
2.1 顯示變數
使用echo命令可以顯示單個變數取值,並在變數名前加$,例如:
echo $LOGNAME
$dave
可以結合使用變數,下面將錯誤資訊和環境變數LOGNAME設定到變數error-msg。
$ERROR_MSG=\"sorry, there is not $LOGNAME\"
$echo \"$ERROR_MSG\"
$sorry, there is not dave上面例子中,shell首先顯示文本,然後尋找變數$LOGNAME,最後擴充變數以顯示整個變數值。
2.2 清除變數
使用unset命令清除變數。
unset variable-name
2.3 顯示所有本地shell變數
使用不加任何參數的set命令顯示所有本地定義的shell變數。
set輸出可能很長。查看輸出時可以看出shell已經設定了一些使用者變數以使工作環境更加容易使用。
2.4 結合變數值
將變數並排可以使變數結合在一起:
echo $var_1$var_2
2.5 測試變數是否已經設定
有時要測試是否已設定或初始設定變數。如果未設定或初始化,就可以使用另一值。此命令格式為:
$ {variable:-value}
意即如果設定了變數值,則使用它,如果未設定,則取新值。例如:
$color=blue
$echo \"The sky is ${color:-grey} today\"
The sky is blue today
變數colour取值blue,echo列印變數colour時,首先查看其是否已賦值,如果查到,則使用該值。現在清除該值,再來看看結果。
$color=blue
$unset color
$echo \"The sky is ${color:-grey} today\"
The sky is grey today上面的例子並沒有將實際值傳給變數,需使用下述命令完成此功能:
$ {variable:=value}
2.6 使用變數來儲存系統命令參數
可以用變數儲存系統命令參數的替換資訊。下面的例子使用變數儲存檔案拷貝的檔案名稱資訊。變數source儲存passwd檔案的路徑。
$ source=\"/etc/passwd\"
$ cd $source
2.7 設定唯讀變數
如果設定變數時,不想再改變其值,可以將之設定為唯讀方式。如果有人包括使用者本人想要改變它,則返回錯誤資訊。格式如下:
variable-name=value
readonly variable-name
設為唯讀後,任何改變其值的操作將返回錯誤資訊。要查看所有唯讀變數,使用命令readonly即可。
3 環境變數
環境變數用於所有使用者進程(經常稱為子進程)。登入進程稱為父進程。s h e l l中執行的使用者進程均稱為子進程。不像本地變數(只用於現在的s h e l l)環境變數可用於所有子進程,這包括編輯器、指令碼和應用。
環境變數可以在命令列中設定,但使用者登出時這些值將丟失,因此最好在. profile檔案中定義。系統管理員可能在/etc/profile檔案中已經設定了一些環境變數。將之放入profile檔案意味著每次登入時這些值 都將被初始化。傳統上,所有環境變數均為大寫。環境變數應用於使用者進程前,必須用export命令匯出。環境變數與本地變數設定方式相同。
3.1 設定環境變數
VARIABLE-NAME=value;export VARIABLE-NAME
在兩個命令之間是一個分號,也可以這樣寫:
VARIABLE-NAME=value
export VARIABLE-NAME
3.2 顯示環境變數
顯示環境變數與顯示本地變數一樣,用echo命令即可。
使用env命令可以查看所有的環境變數。
3.3 清除環境變數
使用unset命令清除環境變數
unset VARIABLE-NAME
3.4 嵌入shell變數
Brourne shell 有一些預留的環境變數名,這些變數名不能用作其他用途。通常在/etc/profile中建立這些嵌入的環境變數,但也不完全是,這取決於使用者自己。以下是嵌入shell變數列表。
1. CDPATH
改變目錄路徑變數,保留一系列由冒號隔開的路徑名,用於cd命令。如果設定了CDPATH,cd一個目錄時,首先尋找CDPATH,如果CDPATH指明此目錄,則此目錄成為當前工作目錄。例子如下:
$ CDPATH=:/home/dave/bin:/usr/local/apps ;export CDPATH.
如果要
$ cd apps
cd命令首先在CDPATH中尋找目錄列表,如果發現apps,則它成為當前工作目錄。
2. EXINIT
EXINIT變數儲存使用vi編輯器時的初始化選項。例如,調用vi時,要顯示行號,且在第10個空格加入tab鍵,命令為:
$ EXINIT=\'set nu tab=10\';export EXINIT
3. HOME
HOME目錄,通常定位於passwd檔案的倒數第2列,用於儲存使用者自身檔案。設定了HOME目錄,可以簡單使用cd命令進入它。也可以用
$ cd $HOME
4. IFS
IFS用作shell指定的預設域分隔字元。原理上講域分隔字元可以是任一字元,但預設通常為空白格、新行或tab鍵。IFS在分隔檔案或變數中各域時很有用。 下面的例子將IFS設定為冒號,然後echo $PATH變數,給出一個目錄分隔開來的可讀性很強的路徑列表。
/sbin /bin /usr/bin /home/dave/bin
要設定其返回初始設定:
$ IFS=<space><tab>; export IFS
這裡<space><tab>為空白格和tab鍵。
5. LOGNAME
此變數儲存登入名稱,應該為預設設定,但如果沒有設定,可使用下面命令完成它:
$ LOGNAME=\'dave\'; export LOGNAME
6. MAIL
MAIL變數儲存郵箱路徑名,預設為/var/spool/mail/<loginname>。shell周期性檢查新郵件,如果有了新郵件,在命令列會出現一個提示資訊。如果郵箱並不在以上指定位置,可以用MAIL設定。
$ MAIL=/usr/mail/dave ;export MAIL
7. MAILCHECK
MAILCHECK預設每60s檢查新郵件,但如果不想如此頻繁檢查新郵件,比如設為每2m,使用命令:
$ MAILCHECK=120 ;export MAILCHECK
8. MAILPATH
如果有多個郵箱要用到MAILPATH,此變數設定將覆蓋MAIL設定。
$ MAILPATH=/var/spool/dave:/var/spool/admin ;export MAILPATH
上面的例子中,MAIL檢測郵箱dave和admin。
9. PATH
PATH變數儲存進行命令或指令碼尋找的目錄順序,正確排列這個次序很重要,可以在執行命令時節省時間。你一定不想在已知命令不存在的目錄下去尋找它。通常 情況,最好首先放在HOME目錄下,接下來是從最常用到一般使用到不常用的目錄列表次序。如果要在當前工作目錄下查詢,無論在哪兒,均可以使用句點操作。 目錄間用冒號分隔,例如:
$ PATH=$HOME/bin:.:/bin:/usr/bin ;export PATH
使用上面的例子首先尋找HOME/bin目錄,然後是當前工作目錄,然後是/bin,最後是
/usr/bin。
PATH可以在系統目錄下/etc/profile中設定,也可以使用下面方法加入自己的尋找目錄。
$PATH=$PATH:/$HOME/bin:.;export PATH
這裡使用了/etc/profile中定義的PATH,並加入$HOME/bin和當前工作目錄。一般來說,在尋找路徑開始使用當前工作目錄不是一個好辦法,這樣很容易被其他使用者發現。
10. PS1
基本提示符包含shell提示符,預設對超級使用者為#,其他為$。可以使用任何符號作提示符.
11. PS2
PS2為附屬提示符,預設為符號>。PS2用於執行多行命令或超過一行的一個命令。
12. SHELL
SHELL變數儲存預設shell,通常在/etc/passwd中已設定,但是如有必要使用另一個shell,
可以用如下方法覆蓋當前shell:
13. TERMINFO
終端初始設定變數儲存終端設定檔的位置。通常在/usr/lib/terminfo或/usr/share/terminfo
$ TERMINFO=/usr/lib/terminfo ;export TERMINFO
14. TERM
TERM變數儲存終端類型。設定TERM使應用獲知終端對螢幕和鍵盤響應的控制序列類型,常用的有vt100、vt200、vt220-8等。
$ TERM=vt100 ;export TERM
15. TZ
時區變數儲存時區值,只有系統管理員才可以更改此設定。例如:
$echo $TZ
GMT2EDT
傳回值表明正在使用格林威治標準時間,與GMT時差為2,並作EDT儲存。
3.5 其他環境變數
還有一些預留的環境變數。其他系統或命令列應用將用到它們。以下是最常用的一些,注意這些值均未有預設設定,必須顯示說明。
1. EDITOR
設定編輯器,最常用。
$ EDITOR=vi ;export EDITOR
2. PWD
目前的目錄路徑名,用cd命令設定此選項。
3. PAGER
儲存螢幕翻頁命令,如pg、more,在查看man文本時用到此功能。
$ PAGER=\'pg -f -p%d\' ;export PAGER
4. MANPATH
儲存系統上man文本的目錄。目錄間用冒號分隔。
$ MANPATH=/usr/apps/man:/usr/local/man ;export MANPATH
5. LPDEST或PRINTER
儲存預設印表機名,用於列印工作時指定印表機名。
$ LPDEST=hp3si-systems
3.6 set命令
在$HOME.profile檔案中設定環境變數時,還有另一種方法匯出這些變數。使用set命令- a選項,即set -a指明所有變數直接被匯出。不要在/etc/profile中使用這種方法,最好只在自己的$HOME/.profile檔案中使用。
3.7 將變數匯出到子進程
shell 新使用者碰到的問題之一是定義的變數如何匯出到子進程。前面已經討論過環境變數的工作方式,現在用指令碼實現它,並在指令碼中調用另一指令碼(這實際上建立了一個 子進程)。以下是兩個指令碼列表father和child。father指令碼設定變數film,取值為AFewGoodMen,並將變數資訊返回螢幕,然後 呼叫指令碼child,這段指令碼顯示第一個指令碼裡的變數film,然後改變其值為DieHard,再將其顯示在螢幕上,最後控制返回father指令碼,再次 顯示這個變數。
$more father
#!/bin/sh
#father script
echo \"this is the father\"
FILM=\"A Few Good Men\"
echo \"I like the film :$FILM\"
#call the child script
./child
echo \"back to father\"
echo \"and the film is :$FILM\"$more child
#!/bin/sh
#child script
echo \"called from father..i am the child\"
echo \"film is :$FILM\"
FILM=\"Die Hard\"
echo \"changing film to :$FILM\"看看指令碼顯示結果:
this is the father
I like the film :A Few Good Men
./child: line 1: child: command not found
called from father..i am the child
film is :
changing film to :Die Hard
back to father
and the film is :A Few Good Men因為在father中並未匯出變數film,因此child指令碼不能將film變數返回。如果在father指令碼中加入export命令,以便child指令碼知道film變數的取值,這就會工作:
$more father2
#!/bin/sh
#father2 script
echo \"this is the father\"
FILM=\"A Few Good Men\"
echo \"I like the film :$FILM\"
#call the child script
#but export varible first
export FILM
./child
echo \"back to father\"
echo \"and the film is :$FILM\"$ ./father2
this is the father
I like the film :A Few Good Men
called from father..i am the child
film is :A Few Good Men
changing film to :Die Hard
back to father
and the film is :A Few Good Men因為在指令碼中加入了export命令,因此可以在任意多的指令碼中使用變數film,它們均繼承了film的所有權。
4 位置變數參數
本章開始提到有4種變數,本地、環境,還有兩種變數被認為是特殊變數,因為它們是唯讀。這兩種變數即為位置變數和特定變數參數。先來看一看位置變數。
如果要向一個shell指令碼傳遞資訊,可以使用位置參數完成此功能。參數相關數目傳入指令碼,此數目可以任意多,但只有前9個可以被訪問,使用shift命 令可以改變這個限制(以後將講到shift命令)。參數從第一個開始,在第9個結束;每個訪問參數前要加$符號。第一個參數為0,表示預留儲存實際指令碼名 字。無論指令碼是否有參數,此值均可用。
如果向指令碼傳送Did You See Th e Full Moon資訊,下面的表格講解了如何訪問每一個參數。
$0 $1 $2 $3 $4 $5 $6 $7 $8 $9
指令碼名字 Did You See The Full Moon
4.1 在指令碼中使用位置參數
在下面指令碼中使用上面的例子。
$more param
#!/bin/sh
#param
echo \"This is the script name : $0\"
echo \"This is the first parameter: $1\"
echo \"This is the 2nd parameter : $2\"
echo \"This is the third parameter: $3\"
echo \"This is the 6th parameter : $6\"
echo \"This is the 7th parameter : $7\"$ ./param Did You See The Full Moon
This is the script name : ./param
This is the first parameter: Did
This is the 2nd parameter : You
This is the third parameter: See
This is the 6th parameter : Moon
This is the 7th parameter :這裡只傳遞6個參數,7、8、9參數為空白,正像預計的那樣。注意,第一個參數表示指令碼名,當從指令碼中處置錯誤資訊時,此參數有很大作用。注意$0返回目前的目錄路徑,如果只返回指令碼名,在basename命令下參數設為$0,剛好得到指令碼名字。
$more param2
#!/bin/sh
echo \"this is `basename $0`calling\"$./param2
this is param2 calling
4.2 向系統命令傳遞參數
可以在指令碼中向系統命令傳遞參數。下面的例子中,在find命令裡,使用$1參數指定尋找檔案名稱。
$more findfile
#!/bin/sh
#findfile
find / -name $1 -print另一個例子中,以$1向grep傳遞一個使用者id號,grep使用此id號在passwd中尋找使用者全名。
$more who_is
#!/bin/sh
#who_is
grep $1 passwd | awk -F: {print $4}
4.3 特定變數參數
既然已經知道了如何訪問和使用shell指令碼中的參數,多知道一點相關資訊也是很有用的,有必要知道指令碼運行時的一些相關控制資訊,這就是特定變數的由來。共有7個特定變數:$# 傳遞到指令碼的參數個數
$* 以一個單字串顯示所有向指令碼傳遞的參數。與位置變數不同,此選項參數可超過9個
$$ 指令碼啟動並執行當前進程ID號
$! 後台啟動並執行最後一個進程的進程ID號
$@ 與$ #相同,但是使用時加引號,並在引號中返回每個參數
$- 顯示shell使用的當前選項,與set命令功能相同
$? 顯示最後命令的退出狀態。0表示沒有錯誤,其他任何值表明有錯誤。 特定變數的輸出使使用者獲知更多的指令碼相關資訊。可以檢查傳遞了多少參數,進程相應的ID號,以免我們想殺掉此進程。
4.4 最後的退出狀態
注意,$?返回0。可以在任何命令或指令碼中返回此變數以獲得返回資訊。基於此資訊,可以在指令碼中做更進一步的研究,返回0意味著成功,1為出現錯誤。
下面的例子拷貝檔案到/tmp,並使用$?檢查結果。
$ cp ok.txt /tmp
$ echo $?
0現在嘗試將一個檔案拷入一個不存在的目錄:
$ cp ok.txt /usr/bin/aaa/bbb
cp: cannot create regular file \'/usr/bin/aaa/bbb\': No such file or directory
$ echo $?
1使 用$?檢驗返回狀態,可知指令碼有錯誤,但同時發現cp:cannot...,因此檢驗最後退出狀態已沒有必要。在指令碼中可以用系統命令處理輸出格式,要求 命令輸出不顯示在螢幕上。為此可以將輸出重新導向到/dev/null,即系統bin中。現在怎樣才能知道指令碼正確與否?好,這時可以用最後退出狀態命令 了。請看上一個例子的此形式的實際操作結果。
$ cp ok.txt /usr/bin/aaa/bbb >/dev/null 2>&1
$ echo $?
1通過將包含錯誤資訊的輸出重新導向到系統bin中,不能獲知最後命令返回狀態,但是通過使用$!,(其傳回值為1)可知指令碼失敗。檢驗指令碼退出狀態時,最好將傳回值設定為一個有意義的名字,這樣可以增加指令碼的可讀性。
4.5 小結
變數可以使shell編程更容易。它能夠儲存輸入值並提高效率。shell變數幾乎可以包含任
何值。特定變數增強了指令碼的功能並提供了傳遞到指令碼的參數的更多資訊。