最簡單的例子 —— Hello World!
幾乎所有的講解編程的書給讀者的第一個例子都是 Hello World 程序,那麼我們今天也就從這個例子出發,來逐步了解 BASH。
用 vi 編輯器編輯一個 hello 文件如下:
#!/bin/bash
# This is a very simple example
echo Hello World
這樣最簡單的一個 BASH 程序就編寫完了。這裡有幾個問題需要說明一下:
一,第一行的 #! 是什麼意思
二,第一行的 /bin/bash 又是什麼意思
三,第二行是註釋嗎
四,echo 語句
五,如何執行該程序
#! 是說明 hello 這個文件的類型的,有點類似於 Windows 系統下用不同文件後綴來表示不同文件類型的意思(但不相同)。linux 系統根據 "#!" 及該字串後面的信息確定該文件的類型,關於這一問題同學們回去以後可以通過 "man magic"命令 及 /usr/share/magic 文件來了解這方面的更多內容。在 BASH 中 第一行的 "#!" 及後面的 "/bin/bash" 就表明該文件是一個 BASH 程序,需要由 /bin 目錄下的 bash 程序來解釋執行。BASH 這個程序一般是存放在 /bin 目錄下,如果你的 Linux 系統比較特別,bash 也有可能被存放在 /sbin 、/usr/local/bin 、/usr/bin 、/usr/sbin 或 /usr/local/sbin 這樣的目錄下;如果還找不到,你可以用 "locate bash" "find / -name bash 2> /dev/null" 或 "whereis bash" 這三個命令找出 bash 所在的位置;如果仍然找不到,那你可能需要自己動手安裝一個 BASH 軟體包了。
第二行的 "# This is a ..." 就是 BASH 程序的註釋,在 BASH 程序中從「#」號(注意:後面緊接著是「!」號的除外)開始到行尾的多有部分均被看作是程序的註釋。的三行的 echo 語句的功能是把 echo 後面的字元串輸出到標準輸出中去。由於 echo 後跟的是 "Hello World" 這個字元串,因此 "Hello World"這個字串就被顯示在控制台終端的屏幕上了。需要注意的是 BASH 中的絕大多數語句結尾處都沒有分號。
如何執行該程序呢?有兩種方法:一種是顯式制定 BASH 去執行:
$ bash hello 或
$ sh hello (這裡 sh 是指向 bash 的一個鏈接,「lrwxrwxrwx 1 root root 4 Aug 20 05:41 /bin/sh -> bash」)
或者可以先將 hello 文件改為可以執行的文件,然後直接運行它,此時由於 hello 文件第一行的 "#! /bin/bash" 的作用,系統會自動用/bin/bash 程序去解釋執行 hello 文件的:
$ chmod u+x hello
$ ./hello
此處沒有直接 「$ hello」是因為當前目錄不是當前用戶可執行文件的默認目錄,而將當前目錄「.」設為默認目錄是一個不安全的設置。
需要注意的是,BASH 程序被執行后,實際上 linux 系統是另外開設了一個進程來運行的。
變數和運算
我們先來從整體上把握一下 BASH 中變數的用法,然後再去分析 BASH 中變數使用與 C 語言中的不同。BASH 中的變數都是不能含有保留字,不能含有 "-" 等保留字元,也不能含有空格。
簡單變數
在 BASH 中變數定義是不需要的,沒有 "int i" 這樣的定義過程。如果想用一個變數,只要他沒有在前面被定義過,就直接可以用,當然你使用該變數的第一條語句應該是對他賦初值了,如果你不賦初值也沒關係,只不過該變數是空( 注意:是 NULL,不是 0 )。不給變數賦初值雖然語法上不反對,但不是一個好的編程習慣。好了我們看看下面的例子:
首先用 vi 編輯下面這個文件 hello2:
#!/bin/bash
# give the initialize value to STR
STR="Hello World"
echo $STR
在上面這個程序中我們需要注意下面幾點:
一,變數賦值時,'='左右兩邊都不能有空格;
二,BASH 中的語句結尾不需要分號(";");
三,除了在變數賦值和在FOR循環語句頭中,BASH 中的變數使用必須在變數前加"$"符號,同學們可以將上面程序中第三行改為 "echo STR" 再試試,看看會出什麼結果。
四,由於 BASH 程序是在一個新的進程中運行的,所以該程序中的變數定義和賦值不會改變其他進程或原始 Shell 中同名變數的值,也不會影響他們的運行。
更細緻的文檔甚至提到以但引號括起來的變數將不被 BASH 解釋為變數,如 '$STR' ,而被看成為純粹的字元串。而且更為標準的變數引用方式是 ${STR} 這樣的,$STR 自不過是對 ${STR} 的一種簡化。在複雜情況下(即有可能產生歧義的地方)最好用帶 {} 的表示方式。
BASH 中的變數既然不需要定義,也就沒有類型一說,一個變數即可以被定義為一個字元串,也可以被再定義為整數。如果對該變數進行整數運算,他就被解釋為整數;如果對他進行字元串操作,他就被看作為一個字元串。請看下面的例子:
#!/bin/bash
x=1999
let "x = $x + 1"
echo $x
x="olympic'"$x
echo $x
關於整數變數計算,有如下幾種:" + - * / % ",他們的意思和字面意思相同。整數運算一般通過 let 和 expr 這兩個指令來實現,如對變數 x 加 1 可以寫作:let "x = $x + 1" 或者 x=`expr $x + 1`
在比較操作上,整數變數和字元串變數各不相同,詳見下表:
對應的操作 整數操作 字元串操作
相同 -eq =
不同 -ne !=
大於 -gt >
小於 -lt <
大於或等於 -ge
小於或等於 -le
為空 -z
不為空 -n
比如:
比較字元串 a 和 b 是否相等就寫作:if [ $a = $b ]
判斷字元串 a 是否為空就寫作: if [ -z $a ]
判斷整數變數 a 是否大於 b 就寫作:if [ $a -gt $b ]
更細緻的文檔推薦在字元串比較時盡量不要使用 -n ,而用 ! -z 來代替。(其中符號 "!" 表示求反操作)
BASH 中的變數除了用於對 整數 和 字元串 進行操作以外,另一個作用是作為文件變數。BASH 是 linux 操作系統的 Shell,因此系統的文件必然是 BASH 需要操作的重要對象,如 if [ -x /root ] 可以用於判斷 /root 目錄是否可以被當前用戶進入。下表列出了 BASH 中用於判斷文件屬性的操作符:
運算符 含義( 滿足下面要求時返回 TRUE )
-e file 文件 file 已經存在
-f file 文件 file 是普通文件
-s file 文件 file 大小不為零
-d file 文件 file 是一個目錄
-r file 文件 file 對當前用戶可以讀取
-w file 文件 file 對當前用戶可以寫入
-x file 文件 file 對當前用戶可以執行
-g file 文件 file 的 GID 標誌被設置
-u file 文件 file 的 UID 標誌被設置
-O file 文件 file 是屬於當前用戶的
-G file 文件 file 的組 ID 和當前用戶相同
file1 -nt file2 文件 file1 比 file2 更新
file1 -ot file2 文件 file1 比 file2 更老
注意:上表中的 file 及 file1、file2 都是指某個文件或目錄的路徑。
關於局部變數
在 BASH 程序中如果一個變數被使用了,那麼直到該程序的結尾,該變數都一直有效。為了使得某個變數存在於一個局部程序塊中,就引入了局部變數的概念。BASH 中,在變數首次被賦初值時加上 local 關鍵字就可以聲明一個局部變數,如下面這個例子:
#!/bin/bash
HELLO=Hello
function hello {
local HELLO=World
echo $HELLO
}
echo $HELLO
hello
echo $HELLO
該程序的執行結果是:
Hello
World
Hello
這個執行結果表明全局變數 $HELLO 的值在執行函數 hello 時並沒有被改變。也就是說局部變數 $HELLO 的影響只存在於函數那個程序塊中。
BASH 中的變數與 C 語言中變數的區別
這裡我們為原來不熟悉 BASH 編程,但是非常熟悉 C 語言的程序員總結一下在 BASH 環境中使用變數需要注意的問題。
1,BASH 中的變數在引用時都需要在變數前加上 "$" 符號( 第一次賦值及在For循環的頭部不用加 "$"符號 );
2,BASH 中沒有浮點運算,因此也就沒有浮點類型的變數可用;
3,BASH 中的整形變數的比較符號與 C 語言中完全不同,而且整形變數的算術運算也需要經過 let 或 expr 語句來處理;
[目錄]
--------------------------------------------------------------------------------
輸入輸出
關於輸入、輸出和錯誤輸出
在字元終端環境中,標準輸入/標準輸出的概念很好理解。輸入即指對一個應用程序或命令的輸入,無論是從鍵盤輸入還是從別的文件輸入;輸出即指應用程序或命令產生的一些信息;與 Windows 系統下不同的是,linux 系統下還有一個標準錯誤輸出的概念,這個概念主要是為程序調試和系統維護目的而設置的,錯誤輸出於標準輸出分開可以讓一些高級的錯誤信息不干擾正常的輸出信息,從而方便一般用戶的使用。
在 linux 系統中:標準輸入(stdin)默認為鍵盤輸入;標準輸出(stdout)默認為屏幕輸出;標準錯誤輸出(stderr)默認也是輸出到屏幕(上面的 std 表示 standard)。在 BASH 中使用這些概念時一般將標準輸出表示為 1,將標準錯誤輸出表示為 2。下面我們舉例來說明如何使用他們,特別是標準輸出和標準錯誤輸出。
輸入、輸出及標準錯誤輸出主要用於 I/O 的重定向,就是說需要改變他們的默認設置。先看這個例子:
$ ls > ls_result
$ ls -l >> ls_result
上面這兩個命令分別將 ls 命令的結果輸出重定向到 ls_result 文件中和追加到 ls_result 文件中,而不是輸出到屏幕上。">"就是輸出(標準輸出和標準錯誤輸出)重定向的代表符號,連續兩個 ">" 符號,即 ">>" 則表示不清除原來的而追加輸出。下面再來看一個稍微複雜的例子:
$ find /home -name lost* 2> err_result
這個命令在 ">" 符號之前多了一個 "2","2>" 表示將標準錯誤輸出重定向。由於 /home 目錄下有些目錄由於許可權限制不能訪問,因此會產生一些標準錯誤輸出被存放在 err_result 文件中。大家可以設想一下 find /home -name lost* 2>>err_result 命令會產生什麼結果?
如果直接執行 find /home -name lost* > all_result ,其結果是只有標準輸出被存入 all_result 文件中,要想讓標準錯誤輸出和標準輸入一樣都被存入到文件中,那該怎麼辦呢?看下面這個例子:
$ find /home -name lost* > all_result 2>& 1
上面這個例子中將首先將標準錯誤輸出也重定向到標準輸出中,再將標準輸出重定向到 all_result 這個文件中。這樣我們就可以將所有的輸出都存儲到文件中了。為實現上述功能,還有一種簡便的寫法如下:
$ find /home -name lost* >& all_result
如果那些出錯信息並不重要,下面這個命令可以讓你避開眾多無用出錯信息的干擾:
$ find /home -name lost* 2> /dev/null
同學們回去后還可以再試驗一下如下幾種重定向方式,看看會出什麼結果,為什麼?
$ find /home -name lost* > all_result 1>& 2
$ find /home -name lost* 2> all_result 1>& 2
$ find /home -name lost* 2>& 1 > all_result
另外一個非常有用的重定向操作符是 "-",請看下面這個例子:
$ (cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xvfp -)
該命令表示把 /source/directory 目錄下的所有文件通過壓縮和解壓,快速的全部移動到 /dest/directory 目錄下去,這個命令在 /source/directory 和 /dest/directory 不處在同一個文件系統下時將顯示出特別的優勢。
下面還幾種不常見的用法:
n<&- 表示將 n 號輸入關閉
<&- 表示關閉標準輸入(鍵盤)
n>&- 表示將 n 號輸出關閉
>&- 表示將標準輸出關閉
[目錄]
--------------------------------------------------------------------------------
流程式控制制
BASH 中的基本流程式控制制語法
BASH 中幾乎含有 C 語言中常用的所有控制結構,如條件分支、循環等,下面逐一介紹。
if...then...else
if 語句用於判斷和分支,其語法規則和 C 語言的 if 非常相似。其幾種基本結構為:
if [ expression ]
then
statments
fi
或者
if [ expression ]
then
statments
else
statments
fi
或者
if [ expression ]
then
statments
else if [ expression ]
then
statments
else
statments
fi
或者
if [ expression ]
then
statments
elif [ expression ]
then
statments
else
statments
fi
值得說明的是如果你將 if 和 then 簡潔的寫在一行裡面,就必須在 then 前面加上分號,如:if [ expression ]; then下面這個例子說明了如何使用 if 條件判斷語句:
#!/bin/bash
if [ $1 -gt 90 ]
then
echo "Good, $1"
elif [ $1 -gt 70 ]
then
echo "OK, $1"
else
echo "Bad, $1"
fi
exit 0
上面例子中的 $1 是指命令行的第一個參數,這個會在後面的「BASH 中的特殊保留字」中講解。
for
for 循環結構與 C 語言中有所不同,在 BASH 中 for 循環的基本結構是:
for $var in
do
statments
done
其中 $var 是循環控制變數, 是 $var 需要遍歷的一個集合,do/done 對包含了循環體,相當於 C 語言中的一對大括弧。另外如果do 和 for 被寫在同一行,必須在 do 前面加上 ";"。如: for $var in ; do 。下面是一個運用 for 進行循環的例子:
#!/bin/bash
for day in Sun Mon Tue Wed Thu Fri Sat
do
echo $day
done
# 如果列表被包含在一對雙引號中,則被認為是一個元素
for day in "Sun Mon Tue Wed Thu Fri Sat"
do
echo $day
done
exit 0
注意上面的例子中,在 for 所在那行的變數 day 是沒有加 "$" 符號的,而在循環體內,echo 所在行變數 $day 是必須加上 "$" 符號的。另外如果寫成 for day 而沒有後面的 in 部分,則 day 將取遍命令行的所有參數。如這個程序:
#!/bin/bash
for param
do
echo $param
done
exit 0
上面這個程序將列出所有命令行參數。for 循環結構的循環體被包含在 do/done 對中,這也是後面的 while、until 循環所具有的特點。
while
while 循環的基本結構是:
while [ condition ]
do
statments
done
這個結構請大家自己編寫一個例子來驗證。
until
until 循環的基本結構是:
until [ condition is TRUE ]
do
statments
done
這個結構也請大家自己編寫一個例子來驗證。
case
BASH 中的 case 結構與 C 語言中的 switch 語句的功能比較類似,可以用於進行多項分支控制。其基本結構是:
case "$var" in
condition1 )
statments1;;
condition2 )
statments2;;
...
* )
default statments;;
esac
下面這個程序是運用 case 結構進行分支執行的例子:
#!/bin/bash
echo "Hit a key, then hit return."
read Keypress
case "$Keypress" in
[a-z] ) echo "Lowercase letter";;
[A-Z] ) echo "Uppercase letter";;
[0-9] ) echo "Digit";;
* ) echo "Punctuation, whitespace, or other";;
esac
exit 0
上面例子中的第四行 "read Keypress" 一句中的 read 語句表示從鍵盤上讀取輸入。這個命令將在本講義的 BASH 的其他高級問題中講解。
break/continue
熟悉 C 語言編程的都很熟悉 break 語句和 continue 語句。BASH 中同樣有這兩條語句,而且作用和用法也和 C 語言中相同,break 語句可以讓程序流程從當前循環體中完全跳出,而 continue 語句可以跳過當次循環的剩餘部分並直接進入下一次循環。
[目錄]
--------------------------------------------------------------------------------
函數
函數的使用
BASH 是一個相對簡單的腳本語言,不過為了方便結構化的設計,BASH 中也提供了函數定義的功能。BASH 中的函數定義很簡單,只要向下面這樣寫就可以了:
function my_funcname {
code block
}
或者
my_funcname() {
code block
}
上面的第二種寫法更接近於 C 語言中的寫法。BASH 中要求函數的定義必須在函數使用之前,這是和 C 語言用頭文件說明函數方法的不同。
更進一步的問題是如何給函數傳遞參數和獲得返回值。BASH 中函數參數的定義並不需要在函數定義處就制定,而只需要在函數被調用時用 BASH 的保留變數 $1 $2 ... 來引用就可以了;BASH 的返回值可以用 return 語句來指定返回一個特定的整數,如果沒有 return 語句顯式的返回一個返回值,則返回值就是該函數最後一條語句執行的結果(一般為 0,如果執行失敗返回錯誤碼)。函數的返回值在調用該函數的程序體中通過 $? 保留字來獲得。下面我們就來看一個用函數來計算整數平方的例子:
#!/bin/bash
square() {
let "res = $1 * $1"
return $res
}
square $1
result=$?
echo $result
exit 0
[目錄]
--------------------------------------------------------------------------------
bash快捷鍵
關於bash在控制台下的快捷鍵
ctrl+u 刪除游標以前的所有字元
ctrl+d 刪除游標以前的一個字元
ctrl+k 刪除游標以後的所有字元
ctrl+h 刪除游標以後的一個字元
ctrl+t 調換游標前兩個字元的次序
ctrl+a 移動游標到最前面
ctrl+e 移動游標到最後面
ctrl+p 上一個命令
ctrl+n 下一個命令
ctrl+s 鎖定輸入
ctrl+q 解除鎖定
ctrl+f 移動游標到后一個字元
ctrl+b 移動游標到前一個字元
ctrl+x 標記一個位置
ctrl+c 清除當前的輸入
[目錄]
--------------------------------------------------------------------------------
gawk
awk是一種程序語言,對文檔資料的處理具有很強的功能。awk名稱是由它三個最初設計者的姓氏的第一個字母而命名的:AlfredV.Aho、PeterJ.Weinberger、BrianW.Kernighan。
awk 最初在1977年完成。1985年發表了一個新版本的awk,它的功能比舊版本增強了不少。awk能夠用很短的程序對文檔里的資料做修改、比較、提取、列印等處理。如果使用C或Pascal等語言代碼編寫完成上述的任務會十分不方便而且很花費時間,所寫的程序也會很大。
awk不僅僅是一個編程語言,它還是linux系統管理員和程序員的一個不可缺少的工具。
awk語言本身十分好學,易於掌握,並且特別的靈活。
gawk是GNU計劃下所做的awk,gawk最初在1986年完成,之後不斷地被改進、更新。gawk包含awk的所有功能。
基本上有兩種方法可以執行gawk程序。
如果gawk程序很短,則可以將gawk直接寫在命令行,如下所示:
gawk'program'input-file1input-file2...
其中program包括一些pattern和action。
如果gawk程序較長,較為方便的做法是將gawk程序存在一個文件中,gawk的格式如下所示:
gawk-fprogram-fileinput-file1input-file2...
gawk程序的文件不止一個時,執行gawk的格式如下所示:
gawk-fprogram-file1-fprogram-file2...input-file1input-file2...
[目錄]
--------------------------------------------------------------------------------
文件、記錄和欄位
一般情況下,gawk可以處理文件中的數值數據,但也可以處理字元串信息。如果數據沒有存儲在文件中,可以通過管道命令和其他的重定向方法給gawk提供輸入。當然,gawk只能處理文本文件(ASCII碼文件)。電話號碼本就是一個gawk可以處理的文件的簡單例子。電話號碼本由很多條目組成,每一個條目都有同樣的格式:姓、名、地址、電話號碼。每一個條目都是按字母順序排列。
在gawk中,每一個這樣的條目叫做一個記錄。它是一個完整的數據的集合。例如,電話號碼本中的SmithJohn這個條目,包括他的地址和電話號碼,就是一條記錄。記錄中的每一項叫做一個欄位。在gawk中,欄位是最基本的單位。多個記錄的集合組成了一個文件。大多數情況下,欄位之間由一個特殊的字元分開,像空格、TAB、分號等。這些字元叫做欄位分隔符。請看下面這個 /etc/passwd文件:
tparker;t36s62hsh;501;101;TimParker;/home/tparker;/bin/bash
etreijs;2ys639dj3h;502;101;EdTreijs;/home/etreijs;/bin/tcsh
ychow;1h27sj;503;101;YvonneChow;/home/ychow;/bin/bash
你可以看出/etc/passwd文件使用分號作為欄位分隔符。/etc/passwd文件中的每一行都包括七個欄位:用戶名;口令;用戶ID;工作組 ID;註釋;home目錄;啟始的外殼。如果你想要查找第六個欄位,只需數過五個分號即可。但考慮到以下電話號碼本的例子,你就會發現一些問題:
SmithJohn13WilsonSt.555-1283
SmithJohn2736ArtsideDrApt123555-2736
SmithJohn125WestmountCr555-1726
雖然我們能夠分辨出每個記錄包括四個欄位,但gawk卻無能為力。電話號碼本使用空格作為分隔符,所以gawk認為Smith是第一個欄位,John是第二個欄位,13是第三個欄位,依次類推。就gawk而言,如果用空格作為欄位分隔符的話,則第一個記錄有六個欄位,而第二個記錄有八個欄位。所以,我們必須找出一個更好的欄位分隔符。例如,像下面一樣使用斜杠作為欄位分隔符:
Smith/John/13WilsonSt./555-1283
Smith/John/2736ArtsideDr/Apt/123/555-2736
Smith/John/125WestmountCr/555-1726
如果你沒有指定其他的字元作為欄位分隔符,那麼gawk將預設地使用空格或TAB作為欄位分隔符。
[目錄]
--------------------------------------------------------------------------------
模式和動作
在gawk語言中每一個命令都由兩部分組成:一個模式(pattern)和一個相應的動作(action)。只要模式符合,gawk就會執行相應的動作。其中模式部分用兩個斜杠括起來,而動作部分用一對花括弧括起來。例如:
/pattern1/{action1}
/pattern2/{action2}
/pattern3/{action3}
所有的gawk程序都是由這樣的一對對的模式和動作組成的。其中模式或動作都能夠被省略,但是兩個不能同時被省略。如果模式被省略,則對於作為輸入的文件裡面的每一行,動作都會被執行。如果動作被省略,則預設的動作被執行,既顯示出所有符合模式的輸入行而不做任何的改動。
下面是一個簡單的例子,因為gawk程序很短,所以將gawk程序直接寫在外殼命令行:
gawk'/tparker/'/etc/passwd
此程序在上面提到的/etc/passwd文件中尋找符合tparker模式的記錄並顯示(此例中沒有動作,所以預設的動作被執行)。
讓我們再看一個例子:
gawk'/UNIX/{print$2}'file2.data
此命令將逐行查找file2.data文件中包含UNIX的記錄,並列印這些記錄的第二個欄位。
你也可以在一個命令中使用多個模式和動作對,例如:
gawk'/scandal/{print$1}/rumor/{print$2}'gossip_file
此命令搜索文件gossip_file中包括scandal的記錄,並列印第一個欄位。然後再從頭搜索gossip_file中包括rumor的記錄,並列印第二個欄位。
[目錄]
--------------------------------------------------------------------------------
運算
gawk有很多比較運算符,下面列出重要的幾個:
==相等
!=不相等
>大於
<小於
>=大於等於
<=小於等於
例如:gawk'$4>100'testfile將會顯示文件testfile中那些第四個欄位大於100的記錄。
下表列出了gawk中基本的數值運算符。
運算符說明示例
+加法運算2+6
-減法運算6-3
*乘法運算2*5
/除法運算8/4
^乘方運算3^2(=9)
%求餘數9%4(=1)
例如:{print$3/2}顯示第三個欄位被2除的結果。
在gawk 中,運算符的優先權和一般的數學運算的優先權一樣。例如:{print$1+$2*$3}顯示第二個欄位和第三個欄位相乘,然後和第一個欄位相加的結果。你也可以用括弧改變優先次序。例如:{print($1+$2)*$3}顯示第一個欄位和第二個欄位相加,然後和第三個欄位相乘的結果。
[目錄]
--------------------------------------------------------------------------------
內部函數
gawk中有各種的內部函數,現在介紹如下:
sqrt(x)求x的平方根
sin(x)求x的正弦函數
cos(x)求x的餘弦函數
atan2(x,y)求x/y的餘切函數
log(x)求x的自然對數
exp(x)求x的e次方
int(x)求x的整數部分
rand()求0和1之間的隨機數
srand(x)將x設置為rand()的種子數
index(in,find)在字元串in中尋找字元串find第一次出現的地方,返回值是字元串find出現在字元串in裡面的位置。如果在字元串in裡面找不到字元串find,則返回值為0。
例如:printindex("peanut","an")
顯示結果3。
length(string)求出string有幾個字元。
例如:length("abcde")
顯示結果5。
match (string,regexp)在字元串string中尋找符合regexp的最長、最靠左邊的子字元串。返回值是regexp在string的開始位置,即index值。match函數將會設置系統變數RSTART等於index的值,系統變數RLENGTH等於符合的字元個數。如果不符合,則會設置 RSTART為0、RLENGTH為-1。
sprintf(format,expression1,...)和printf類似,但是sprintf並不顯示,而是返回字元串。
例如:sprintf("pi=%.2f(approx.)",22/7)
返回的字元串為pi=3.14(approx.)
sub(regexp,replacement,target)在字元串target中尋找符合regexp的最長、最靠左的地方,以字串replacement代替最左邊的regexp。
例如:
str="water,water,everywhere"
sub(/at/,"ith",str)
結果字元串str會變成
wither,water,everywhere
gsub(regexp,replacement,target)與前面的sub類似。在字元串target中尋找符合regexp的所有地方,以字元串replacement代替所有的regexp。
例如:str="water,water,everywhere"
gsub(/at/,"ith",str)
結果字元串str會變成
wither,wither,everywhere
substr(string,start,length)返回字元串string的子字元串,這個子字元串的長度為length,從第start個位置開始。
例如:substr("washington",5,3)返回值為ing如果沒有length,則返回的子字元串是從第start個位置開始至結束。
例如:substr("washington",5)
返回值為ington。
tolower(string)將字元串string的大寫字母改為小寫字母。
例如:tolower("MiXeDcAsE123")
返回值為mixedcase123。
toupper(string)將字元串string的小寫字母改為大寫字母。
例如:toupper("MiXeDcAsE123")
返回值為MIXEDCASE123。
輸入輸出的內部函數
close(filename)將輸入或輸出的文件filename關閉。
system(command)此函數允許用戶執行操作系統的指令,執行完畢后將回到gawk程序。
例如:BEGIN{system("ls")}
[目錄]
--------------------------------------------------------------------------------
字元串和數字
字元串就是一連串的字元,它可以被gawk逐字地翻譯。字元串用雙引號括起來。數字不能用雙引號括起來,並且gawk將它當作一個數值。
例如:gawk'$1!="Tim"{print}'testfile
此命令將顯示第一個欄位和Tim不相同的所有記錄。如果命令中Tim兩邊不用雙引號,gawk將不能正確執行。
再如:gawk'$1=="50"{print}'testfile
此命令將顯示所有第一個欄位和50這個字元串相同的記錄。gawk不管第一欄位中的數值的大小,而只是逐字地比較。這時,字元串50和數值50並不相等。
我們可以讓動作顯示一些比較複雜的結果。例如:
gawk'$1!="Tim"{print$1,$5,$6,$2}'testfile
你也可以使用一些換碼控制符格式化整行的輸出。之所以叫做換碼控制符,是因為gawk對這些符號有特殊的解釋。下面列出常用的換碼控制符:
a警告或響鈴字元。
後退一格。
f換頁。
換行。
回車。
Tab。
v垂直的tab。
在gawk中,預設的欄位分隔符一般是空格符或TAB。但你可以在命令行使用-F選項改變字元分隔符,只需在-F後面跟著你想用的分隔符即可。
gawk-F";"'/tparker/{print}'/etc/passwd
在此例中,你將字元分隔符設置成分號。注意:-F必須是大寫的,而且必須在第一個引號之前。
[目錄]
--------------------------------------------------------------------------------
元字元
gawk語言在格式匹配時有其特殊的規則。例如,cat能夠和記錄中任何位置有這三個字元的欄位匹配。但有時你需要一些更為特殊的匹配。如果你想讓cat只和concatenate匹配,則需要在格式兩端加上空格:
/cat/{print}
再例如,你希望既和cat又和CAT匹配,則可以使用或(|):
/cat|CAT/{print}
在gawk中,有幾個字元有特殊意義。下面列出可以用在gawk格式中的這些字元:
?^表示欄位的開始。
例如:
$3~/^b/
如果第三個欄位以字元b開始,則匹配。
?$表示欄位的結束。
例如:
$3~/b$/
如果第三個欄位以字元b結束,則匹配。
?.表示和任何單字元m匹配。
例如:
$3~/i.m/
如果第三個欄位有字元i,則匹配。
?|表示「或」。
例如:
/cat|CAT/
和cat或CAT字元匹配。
?*表示字元的零到多次重複。
例如:
/UNI*X/
和UNX、UNIX、UNIIX、UNIIIX等匹配。
?+表示字元的一次到多次重複。
例如:
/UNI+X/
和UNIX、UNIIX等匹配。
?{a,b}表示字元a次到b次之間的重複。
例如:
/UNI{1,3}X
和UNIX、UNIIX和UNIIIX匹配。
??表示字元零次和一次的重複。
例如:
/UNI?X/
和UNX和UNIX匹配。
?[]表示字元的範圍。
例如:
/I[BDG]M/
和IBM、IDM和IGM匹配
?[^]表示不在[]中的字元。
例如:
/I[^DE]M/
和所有的以I開始、M結束的包括三個字元的字元串匹配,除了IDM和IEM之外。
[目錄]
--------------------------------------------------------------------------------
調用gawk程序
當需要很多對模式和動作時,你可以編寫一個gawk程序(也叫做gawk腳本)。在gawk程序中,你可以省略模式和動作兩邊的引號,因為在gawk程序中,模式和動作從哪開始和從哪結束時是很顯然的。
你可以使用如下命令調用gawk程序:
gawk-fscriptfilename
此命令使gawk對文件filename執行名為script的gawk程序。
如果你不希望使用預設的欄位分隔符,你可以在f選項後面跟著F選項指定新的欄位分隔符(當然你也可以在gawk程序中指定),例如,使用分號作為欄位分隔符:
gawk-fscript-F";"filename
如果希望gawk程序處理多個文件,則把各個文件名羅列其後:
gawk-fscriptfilename1filename2filename3...
預設情況下,gawk的輸出將送往屏幕。但你可以使用linux的重定向命令使gawk的輸出送往一個文件:
gawk-fscriptfilename>save_file
[目錄]
--------------------------------------------------------------------------------
BEGIN和END
有兩個特殊的模式在gawk中非常有用。BEGIN模式用來指明gawk開始處理一個文件之前執行一些動作。BEGIN經常用來初始化數值,設置參數等。END模式用來在文件處理完成後執行一些指令,一般用作總結或註釋。
BEGIN和END中所有要執行的指令都應該用花括弧括起來。BEGIN和END必須使用大寫。
請看下面的例子:
BEGIN{print"Startingtheprocessthefile"}
$1=="UNIX"{print}
$2>10{printf"Thislinehasavalueof%d",$2}
END{print"Finishedprocessingthefile.Bye!"}
此程序中,先顯示一條信息:Startingtheprocessthefile,然後將所有第一個欄位等於UNIX的整條記錄顯示出來,然後再顯示第二個欄位大於10的記錄,最後顯示信息:Finished processingthefile.Bye!。
[目錄]
--------------------------------------------------------------------------------
變數
在gawk中,可以用等號(=)給一個變數賦值:
var1=10
在gawk中,你不必事先聲明變數類型。
請看下面的例子:
$1=="Plastic"{count=count+1}
如果第一個欄位是Plastic,則count的值加1。在此之前,我們應當給count賦予過初值,一般是在BEGIN部分。
下面是比較完整的例子:
BEGIN{count=0}
$5=="UNIX"{count=count+1}
END{printf"%doccurrencesofUNIXwerefound",count}
變數可以和欄位和數值一起使用,所以,下面的表達式均為合法:
count=count+$6
count=$5-8
count=$5+var1
變數也可以是格式的一部分,例如:
$2>max_value{print"Maxvalueexceededby",$2-max_value}
$4-var1
gawk語言中有幾個十分有用的內置變數,現在列於下面:
NR已經讀取過的記錄數。
FNR從當前文件中讀出的記錄數。
FILENAME輸入文件的名字。
FS欄位分隔符(預設為空格)。
RS記錄分隔符(預設為換行)。
OFMT數字的輸出格式(預設為%g)。
OFS輸出欄位分隔符。
ORS輸出記錄分隔符。
NF當前記錄中的欄位數。
如果你只處理一個文件,則NR和FNR的值是一樣的。但如果是多個文件,NR是對所有的文件來說的,而FNR則只是針對當前文件而言。
例如:
NR<=5{print"Notenoughfieldsintherecord"}
檢查記錄數是否小於5,如果小於5,則顯示出錯信息。
FS十分有用,因為FS控制輸入文件的欄位分隔符。例如,在BEGIN格式中,使用如下的命令:
FS=":"
[目錄]
--------------------------------------------------------------------------------
控制結構
if表達式
if表達式的語法如下:
if(expression){
commands
}
else{
commands
}
例如:
#asimpleifloop
(if($1==0){
print"This cell has a value of zero"
}
else{
printf"The value is %d ",$1
})
再看下一個例子:
#anicely form attedi floop
(if($1>$2){
print"The first column is larger"
}
else{
print"The second column is larger"
})
while循環
while循環的語法如下:
while(expression){
commands
}
例如:
#interest calculation computes compound interest
#inputs from a file arethea mount,interest_rateandyears
{var=1
while(var<=$3){
printf("%f ",$1*(1+$2)^var)
var++
for循環
for循環的語法如下:
for(initialization;expression;increment){
command
}
例如:
#interest calculation computes compound interest
#inputs from a fil earethea mount,interest_rateandyears
{for(var=1;var<=$3;var++){
printf("%f ",$1*(1+$2)^var)
}}
next和exit
next指令用來告訴gawk處理文件中的下一個記錄,而不管現在正在做什麼。語法如下:
{command1
command2
command3
next
command4
}
程序只要執行到next指令,就跳到下一個記錄從頭執行命令。因此,本例中,command4指令永遠不會被執行。
程序遇到exit指令后,就轉到程序的末尾去執行END,如果有END的話。
[目錄]
--------------------------------------------------------------------------------
數組
gawk語言支持數組結構。數組不必事先初始化。聲明一個數組的方法如下:
arrayname[num]=value
請看下面的例子:
#reverse lines in a file
{line[NR]=$0} #remember each line
END{var=NR #output lines in reverse order
while(var>0){
printline[var]
var--
}
此段程序讀取一個文件的每一行,並用相反的順序顯示出來。我們使用NR作為數組的下標來存儲文件的每一條記錄,然後在從最後一條記錄開始,將文件逐條地顯示出來。
[目錄]
--------------------------------------------------------------------------------
自定義函數
用戶自定義函數
複雜的gawk程序常常可以使用自己定義的函數來簡化。調用用戶自定義函數與調用內部函數的方法一樣。函數的定義可以放在gawk程序的任何地方。
用戶自定義函數的格式如下:
functionname(parameter-list){
body-of-function
}
name 是所定義的函數的名稱。一個正確的函數名稱可包括一序列的字母、數字、下標線(underscores),但是不可用數字做開頭。parameter- list是函數的全部參數的列表,各個參數之間以逗點隔開。body-of-function包含gawk的表達式,它是函數定義里最重要的部分,它決定函數實際要做的事情。
下面這個例子,會將每個記錄的第一個欄位的值的平方與第二個欄位的值的平方加起來。
{print"sum=",SquareSum($1,$2)}
function SquareSum(x,y){
sum=x*x+y*y
returnsum
}
[目錄]
--------------------------------------------------------------------------------
幾個實例
最後,再舉幾個gawk的例子:
gawk'{if(NF>max)max=NF}
END{printmax}'
此程序會顯示所有輸入行之中欄位的最大個數。
gawk'length($0)>80'
此程序會顯示出超過80個字元的每一行。此處只有模式被列出,動作是採用預設值顯示整個記錄。
gawk'NF>0'
顯示擁有至少一個欄位的所有行。這是一個簡單的方法,將一個文件里的所有空白行刪除。
gawk'BEGIN{for(i=1;i<=7;i++)
printint(101*rand())}'
此程序會顯示出範圍是0到100之間的7個隨機數。
ls-lfiles|gawk'{x+=$4};END{print"totalbytes:"x}'
此程序會顯示出所有指定的文件的總位元組數。
expandfile|gawk'{if(x END{print"maximumlinelengthis"x}'
此程序會將指定文件里最長一行的長度顯示出來。expand會將tab改成space,所以是用實際的右邊界來做長度的比較。
gawk'BEGIN{FS=":"}
{print$1|"sort"}'/etc/passwd
此程序會將所有用戶的登錄名稱,依照字母的順序顯示出來。
gawk'{nlines++}
END{printnlines}'
此程序會將一個文件的總行數顯示出來。
gawk'END{printNR}'
此程序也會將一個文件的總行數顯示出來,但是計算行數的工作由gawk來做。
gawk'{printNR,$0}'
此程序顯示出文件的內容時,會在每行的最前面顯示出行號,它的函數與『cat-n』類似。
[目錄]
--------------------------------------------------------------------------------
Perl
Perl的基本特點
「別期望在一刻鐘內就能領略Perl的所有神奇之處,這種情況很像吃香蕉,用不著吃完整隻香蕉后才知其味,每咬一口都是享受,並促使你再咬下一口,再下一口。」上面這段話是Perl項目發起人勞利·華爾(LarryWall)對學習Perl語言的一段經典評論,希望大家都能找到這種感覺。
Perl的設計目標是幫助UNIX用戶完成一些常見的任務,這些任務對於Shell來說過於沉重或對移植性要求過於嚴格。Perl語言中包含了C、C+ +、shell,script、sed、awk這幾個語言的語法,它最初的目的就是用來取代UNIX中sed/awk與腳本語言的組合,用來彙整信息,產生報表。因此Perl語言要遠遠比前面講的BASH複雜和功能強大。Perl的設計原則或者說Perl的設計哲學是以實用為第一優先,也就是力圖使 Perl語言容易使用、有效率、而且完整。
Perl是按GNUPublicLicense和ArticticLicense兩種許可證形式分發的,其實質是開源軟體、自由軟體的,原先運行於UNIX和類UNIX系統,現在已可以方便地在OS/2,Windows9x,Windows/NT等系統下運行。Perl是一種解釋運行的語言,和BASH程序一樣,一般Perl程序的第一行需註明自己是一個Perl程序而不是Shell程序,所以一般將下面一行語句:
#!/usr/bin/perl 作為文件的第一行。
Perl由於引入了模塊的設計思想,隨著版本的改進,功能越來越強。現在Perl的功能已經超乎原先設計時的想象,幾乎任何事都可以做到,也變成每一部工作站必備的標準工具了。Perl最為著名的一點就是他對字元串的處理,由於Internet對文字信息處理的巨大需求,使得Perl的應用如日中天,而且Perl語言也的確是一個非常優秀的文字信息處理語言。
一個有用的Perl程序可以很短。例如希望更換大量文件中的一些相同內容,可以使用下面的一條命令:
perl-e's/gopher/WorldWideWeb/gi'-p-i.bak*.html
下面是一個基本的perl程序:
#!/usr/local/bin/perl
#
#Programtodotheobvious
print'Helloworld.';#只是簡單地顯示出Helloworld.字元串。
[目錄]
--------------------------------------------------------------------------------
變數
Perl中有三種變數:標量,數組(列表)和相關數組。
Perl中最基本的變數類型是標量。標量既可以是數字,也可以是字元串,而且兩者是可以互換的。具體是數字還是字元串,可以有上下文決定。標量變數的語法為$variable_name。例如:
$priority=9;
把9賦予標量變數$priority,你也可以將字元串賦予該變數:
$priority='high';
注意在Perl中,變數名的大小寫是敏感的,所以$a和$A是不同的變數。
以下的數值或字元串都可以賦給標量:
12312.45E-100xff(hex)0377(octal)
'Whatyou$seeis(almost)what youget''Don'tWalk'
"Howareyou?""Substitutevaluesof$xand in"quotes."
`date``uptime-u``du-sk$filespec|sort-n`
$x$list_of_things[5]$lookup{'key'}
從上面可以看出,Perl中有三種類型的引用。雙引號("")括起來的字元串中的任何標量和特殊意義的字元都將被Perl解釋。如果不想讓Perl解釋字元串中的任何標量和特殊意義的字元,應該將字元串用單括弧括起來。這時,Perl不解釋其中的任何字元,除了\和'。最後,可以用(`)將命令括起來,這樣,其中的命令可以正常運行,並能得到命令的返回值。請看下面的例子:
1#!/usr/bin/perl
2$folks="100";
3print"$folks=$folks ";
4print'$folks=$folks ';
5print" BEEP!aLSOMEBLANKELINESHERE ";
6$date=`date+%D`;
7print"Todayis[$date] ";
8chop$date;
9print"Dateafterchoppingoffcarriagereturn:[".$date."] ";
注意實際程序中不應該包括行號。其輸出結果如下:
$folks=100
$folks=$folks
BEEP!someblankLINESHERE
Todayis[03/29/96]
Dateafterchoppingoffcarriagereturn:[03/29/96]
第3行顯示$folks的值。$之前必須使用換碼符,以便Perl顯示字元串$folks而不是$folks的值100。
第4行使用的是單引號,結果Perl不解釋其中的任何內容,只是原封不動地將字元串顯示出來。
第6行使用的是(`),則date+%D命令的執行結果存儲在標量$date中。
上例中使用了一些有特殊意義的字元,下面列出這些字元的含義:
換行。
回車。
製表符。
a蜂鳴聲。
Backspace。
LE將L和E之間的字元轉換成小寫。
l將其後的字元轉換成小寫。
UE將U和E之間的字元轉換成大寫。
u將其後的字元轉換成大寫。
cC插入控制字元C。
x##十六進位數##。
ooo八進位數ooo。
\反斜杠。
按原樣輸出下一個字元,例如:$輸出$。
Perl中的數字是以浮點形式存儲的。下面列出有關的數字運算符:
$a=1+2;#1加2,結果存儲在$a中。
$a=3-4;#3減4,結果存儲在$a中。
$a=5*6;#5乘6,結果存儲在$a中。
$a=7/8;#7除以8,結果存儲在$a中。
$a=9**10;#9的10次方,結果存儲在$a中。
$a=5%2;#取5除2的餘數,結果存儲在$a中。
++$a;#$a加1,然後賦予$a。
$a++;#先將$a返回,然後$a加1。
--$a;#$a減1,然後賦予$a。
$a--;#先將$a返回,然後$a減1。
Perl支持的邏輯運算符:
$r=$x||$y$r=$x或$y。
$r=$x&&$y$r=$x與$y。
$r=!$x$r=非$x。
對於字元標量,Perl支持下面的運算符:
$a=$b.$c;#將$b和$c連接,然後賦予$a。
$a=$bx$c;#$b重複$c次,然後賦予$a。
下面是Perl中的賦值方法:
$a=$b;#將$b賦予$a。
$a+=$b;#$b加$a,然後賦予$a。
$$a-=$b;#$a減$b,然後賦予$a。
$a.=$b;#把$b連接到$a的後面,然後賦予$a。
你也可以使用下面的比較運算符:
$x==$y如果$x和$y相等,則返回真。
$x!=$y如果$x和$y不相等,則返回真。
$x<$y如果$x比$y小,則返回真。
$x<=$y如果$x小於等於$y,則返回真。
$x>$y如果$x比$y大,則返回真。
$x>=$y如果$x大於等於$y,則返回真。
$xeq$y如果字元串$x和字元串$y相同,則返回真。
數組
數組也叫做列表,是由一系列的標量組成的。數組變數以@開頭。請看以下的賦值語句:
@food=("apples","pears","eels");
@music=("whistle","flute");
數組的下標從0開始,你可以使用方括弧引用數組的下標:
$food[2]
返回eels。注意@已經變成了$,因為eels是一個標量。
在Perl中,數組有多種賦值方法,例如:
@moremusic=("organ",@music,"harp");
@moremusic=("organ","whistle","flute","harp");
還有一種方法可以將新的元素增加到數組中:
push(@food,"eggs");
把eggs增加到數組@food的末端。要往數組中增加多個元素,可以使用下面的語句:
push(@food,"eggs","lard");
push(@food,("eggs","lard"));
push(@food,@morefood);
push返回數組的新長度。
pop用來將數組的最後一個元素刪除,並返回該元素。例如:
@food=("apples","pears","eels");
$grub=pop(@food);#此時$grub="eels"
請看下面的例子:
1#!/usr/bin/perl
2#
3#AnexampletoshowhowarraysworkinPerl
4#
[email protected]=(10,24,39);
[email protected]=('computer','rat',"kbd");
7
8$a=1;$b=2;$c='3';
[email protected]=($a,$b,$c);
10
[email protected]=();
12
[email protected][email protected];
14
15print'@amounts=';
16print"@amounts ";
17
18print'@parts=';
19print"@parts ";
20
21print'@count=';
22print"@count ";
23
24print'@empty=';
25print"@empty ";
26
27print'@spare=';
28print"@spare ";
29
30
31#
32#Accessingindividualitemsinanarray
33#
34print'$amounts[0]=';
35print"$amounts[0] ";
36print'$amounts[1]=';
37print"$amounts[1] ";
38print'$amounts[2]=';
39print"$amounts[2] ";
40print'$amounts[3]=';
41print"$amounts[3] ";
42
43print"[email protected]=$#amounts ";
[email protected];print"SizeofAmount=$size ";
45print"[email protected]=$amounts[$[] ";
以下是顯示結果:
@amounts=102439
@parts=computerratkbd
@count=123
@empty=
@spare=computerratkbd
$amounts[0]=10
$amounts[1]=24
$amounts[2]=39
$amounts[3]=
[email protected]=2
SizeofAmount=3
Item
第5 行,三個整數值賦給了數組@amounts。第6行,三個字元串賦給了數組@parts。第8行,字元串和數字分別賦給了三個變數,然後將三個變數賦給了數組@count。11行創建了一個空數組。13行將數組@spare賦給了數組@parts。15到28行輸出了顯示的前5行。34到41行分別存取數組@amounts的每個元素。注意$amount[3]不存在,所以顯示一個空項。43行中使用$#array方式顯示一個數組的最後一個下標,所以數組@amounts的大小是($#amounts+1)。44行中將一個數組賦給了一個標量,則將數組的大小賦給了標量。45行使用了一個Perl中的特殊變數$[,用來表示一個數組的起始位置(預設為0)。
相關數組
一般的數組允許我們通過數字下標存取其中的元素。例如數組 @food的第一個元素是$food[0],第二個元素是$food[1],以此類推。但Perl允許創建相關數組,這樣我們可以通過字元串存取數組。其實,一個相關數組中每個下標索引對應兩個條目,第一個條目叫做關鍵字,第二個條目叫做數值。這樣,你就可以通過關鍵字來讀取數值。相關數組名以百分號 (%)開頭,通過花括弧({})引用條目。例如:
%ages=("MichaelCaine",39,
"DirtyDen",34,
"Angie",27,
"Willy","21indogyears",
"TheQueenMother",108);
這樣我們可以通過下面的方法讀取數組的值:
$ages{"MichaelCaine"};#Returns39
$ages{"DirtyDen"};#Returns34
$ages{"Angie"};#Returns27
$ages{"Willy"};#Returns"21indogyears"
$ages{"TheQueenMother"};#Returns108
[目錄]
--------------------------------------------------------------------------------
文件操作
文件句柄和文件操作
我們可以通過下面的程序了解一下文件句柄的基本用法。此程序的執行結果和UNIX系統
的cat命令一樣:
#!/usr/local/bin/perl
#
#Programtoopenthepasswordfile,readitin,
#printit,andcloseitagain.
$file='/etc/passwd';#Namethefile
open(INFO,$file);#Openthefile
@lines=;#Readitintoanarray
close(INFO);#Closethefile
[email protected];#Printthearray
open函數打開一個文件以供讀取,其中第一個參數是文件句柄(filehandle)。文件句柄用來供Perl在以後指向該文件。第二個參數指向該文件的文件名。close函數關閉該文件。
open函數還可以用來打開文件以供寫入和追加,只須分別在文件名之前加上>和>>:
open(INFO,$file);#Openforinput
open(INFO,">$file");#Openforoutput
open(INFO,">>$file");#Openforappending
open(INFO,"<$file");#Alsoopenforinput
另外,如果你希望輸出內容到一個已經打開供寫入的文件中,你可以使用帶有額外參數的print語句。例如:
printINFO"Thislinegoestothefile. ";
最後,可以使用如下的語句打開標準輸入(通常為鍵盤)和標準輸出(通常為顯示器):
open(INFO,'-');#Openstandardinput
open(INFO,'>-');#Openstandardoutput
一個Perl程序在它一啟動時就已經有了三個文件句柄:STDIN(標準輸入設備),STDOUT(標準輸出設備)和STDERR(標準錯誤信息輸出設備)。如果想要從一個已經打開的文件句柄中讀取信息,可以使用<>運算符。
使用read和write函數可以讀寫一個二進位的文件。其用法如下:
read(HANDLE,$buffer,$length[,$offset]);
此命令可以把文件句柄是HANDLE的文件從文件開始位移$offset處,共$length位元組,讀到$buffer中。其中$offset是可選項,如果省略$offset,則read()從當前位置的前$length個位元組讀取到當前位置。可以使用下面的命令查看是否到了文件末尾:
eof(HANDLE);
如果返回一個非零值,則說明已經到達文件的末尾。
打開文件時可能出錯,所以可以使用die()顯示出錯信息。下面打開一個叫做「test.data」的文件:
open(TESTFILE,"test.data")||die" $0Cannotopen$! ";
[目錄]
--------------------------------------------------------------------------------
流程式控制制
foreach循環
在Perl中,可以使用foreach循環來讀取數組或其他類似列表結構中的每一行。請看下面的例子:
foreach$morsel(@food)#Visiteachiteminturn
#andcallit$morsel
{
print"$morsel ";#Printtheitem
print"Yumyum ";#Thatwasnice
}
每次要執行的命令用花括弧括出。第一次執行時$morsel被賦予數組@food的第一個元素的值,第二次執行時$morsel被賦予數組@food的第二個元素的值,以此類推直到數組的最後一個元素。
判斷運算
在Perl中任何非零的數字和非空的字元串都被認為是真。零、全為零的字元串和空字元串都為假。
下面是一些判斷運算符:
$a==$b如果$a和$b相等,則返回真。
$a!=$b如果$a和$b不相等,則返回真。
$aeq$b如果字元串$a和字元串$b相同,則返回真
$ane$b如果字元串$a和字元串$b不相同,則返回真。
你可以使用邏輯運算符:
($a&&$b)$a與$b。
($a||$b)$a或$b。
!($a)非$a。
for循環
Perl中的for結構和C語言中的for結構基本一樣:
for(initialise;test;inc){
first_action;
second_action;
etc
}
下面是一個for循環的例子,用來顯示從0到9的數字:
for($i=0;$i<10;++$i)#Startwith$i=1
#Doitwhile$i<10
#Increment$ibeforerepeating
{
print"$i ";
}
while和until循環
下面是一個while和until循環的例子。它從鍵盤讀取輸入直到得到正確的口令為止。
#!/usr/local/bin/perl
print"Password?";#Askforinput
$a=;#Getinput
chop$a;#Removethenewlineatend
while($ane"fred")#Whileinputiswrong...
{
print"sorry.Again?";#Askagain
$a=;#Getinputagain
chop$a;#Chopoffnewlineagain
}
當輸入和口令不相等時,執行while循環。
你也可以在執行體的末尾處使用while和until,這時需要用do語句:
#!/usr/local/bin/perl
do
{
"Password?";#Askforinput
$a=;#Getinput
chop$a;#Chopoffnewline
}
while($ane"fred")#Redowhilewronginput
條件結構
Perl也允許if/then/else表達式。請看下面的例子:
if($a){
print"Thestringisnotempty ";
}
else{
print"Thestringisempty ";
}
注意在Perl中,空字元被認為是假。
If結構中也可以使用嵌套結構:
if(!$a)#The!isthenotoperator
{
print"Thestringisempty ";
}
elsif(length($a)==1)#Ifabovefails,trythis
{
print"Thestringhasonecharacter ";
}
elsif(length($a)==2)#Ifthatfails,trythis
{
print"Thestringhastwocharacters ";
}
else#Now,everythinghasfailed
{
print"Thestringhaslotsofcharacters ";
[目錄]
--------------------------------------------------------------------------------
字元匹配
Perl字元匹配功能十分強大。字元匹配功能的核心是規則表達式(RE),也就是字元匹配過程中涉及到的格式。=~運算符用來進行格式匹配和替換。例如:
如果:
$s='Oneifbylandandtwoifbysea';
則:
if($s=~/ifbyla/){print"YES"}
else{print"NO"}
將會顯示YES,因為ifbyla在字元串$s中。再例如:
if($s=~/one/){print"YES"}
else{print"NO"}
將顯示NO,因為RE是對大小寫敏感的。如果使用i選項,則將忽略大小寫,則下面會顯示出YES:
if($s=~/one/i){print"YES"}
else{print"NO"}
下面列出了RE中許多具有特殊意義的字元:
.任何字元除了換行符()
^一行和一個字元串的開始
$一行和一個字元串的結束
*其前一個字元重複零次或多次
+其前一個字元重複一次或多次
?其前一個字元重複零次或一次
例如:
if($x=~/l.mp/){print"YES"}
對於$x=「lamp」、「lump」、「slumped」將顯示YES,但對於$x=「lmp」或「lessamperes」將不會顯示YES。
再看下面的例子:
/fr.*nd/匹配frnd、friend、frontandback。
/fr.+nd/匹配frond、friend、frontandback。但不匹配frnd。
/10*1/匹配11、101、1001、100000001。
/b(an)*a/匹配ba、bana、banana、banananana。
/flo?at/匹配flat和float,但不匹配flooat。
方括弧用來匹配其中的任何字元。方括弧中的-符號用來表明在什麼之間,^符號表明非的意思。
[0123456789]匹配任何單個的數字。
[0-9]匹配任何單個的數字。
[a-z]+匹配任何由小寫字母組成的單詞。
[^0-9]匹配任何非數字的字元。
反斜杠還是用於轉義。如果你想匹配+、?、.、*、^、$、(、)、[、]、{、}、|、和/這些字元,則其前面必須用反斜杠()。例如:
/10.2/匹配10Q2、1052和10.2。
/10.2/匹配10.2,但不和10Q2或1052匹配。
/*+/匹配一個或多個星號。
/A:\DIR/匹配A:DIR。
//usr/bin/匹配/usr/bin。
下面還有一些特殊意義的字元:
換行。
製表符。
w任何字母和數字和[a-zA-Z0-9_]一樣。
W任何非字母和數字和[^a-zA-Z0-9_]一樣。
d任何數字和[0-9]一樣。
D任何非數字和[^0-9]一樣。
s任何空白字元:空格、tab、換行等。
S任何非空白字元。
單詞邊界,只對[]以外有效。
B非單詞邊界。
替換
Perl可以使用s函數利用字元匹配的結果進行字元替換。s函數和vi編輯器的作用基本一樣。這時還是使用字元匹配運算符=~,例如:將字元串$sentence中所出現的london用London替換,可以使用如下的命令:
$sentence=~s/london/London/
命令的返回值是所做的替換數目。
但此命令只能替換第一個出現的london。如果希望將所有在字元串中出現的london都用London替換,則應使用/g選項:
s/london/London/g
此命令的對象是$_變數,也就是當前的預設變數。
如果希望還能替換類似lOndon、lonDON、LoNDoN的字元串,可以使用:
s/[Ll][Oo][Nn][Dd][Oo][Nn]/London/g
但更為簡單的方法是使用i選項,也就是忽略大小寫:
s/london/London/gi
翻譯
tr函數允許逐字地翻譯。下面的命令使得字元串$sentence中的a、b、c分別由e、f、d代替:
$sentence=~tr/abc/efd/
結果返回所做的替換數目。
大多數RE中的特殊字元在tr函數中並不存在。例如下面的命令用來計算字元串$sentence中星號(*)的數目,並將結果存儲在$count:
$count=($sentence=~tr/*/*/);
[目錄]
--------------------------------------------------------------------------------
子過程
子過程的定義
Perl語言也可以定義自己的子過程。子過程的定義如下:
submysubroutine{
print"Notaveryinterestingroutine ";
print"Thisdoesthesamethingeverytime ";
}
下面的幾種方法都可以調用這個子過程:
Callthesubroutine
&mysubroutine($_);#Callitwithaparameter
&mysubroutine(1+2,$_);#Callitwithtwoparameters
參數
調用一個子過程時,所有的參數都傳送到了數組@_中。下面子過程的例子顯示出所有的
參數:
subprintargs{
print"@_ ";
}
&printargs("perly","king");#Exampleprints"perlyking"
&printargs("frog","and","toad");#Prints"frogandtoad"
返回值
下面的例子返回兩個輸入參數的最大值:
submaximum{
if($_[0]>$_[1]){
$_[0];
}
else{
$_[1];
$biggest=&maximum(37,24);#Now$biggestis37
[目錄]
--------------------------------------------------------------------------------
例子
最後請看一個Perl語言的完整的例子。
此程序從一個記錄學生信息的文件stufile和一個記錄學生成績的文件scorefile中生成一個
學生成績報告單。
輸入文件stufile由學生ID、姓名和年級三個欄位組成,其間由分號隔開:
123456;Washington,George;SR
246802;Lincoln,Abraham"Abe";SO
357913;Jefferson,Thomas;JR
212121;Roosevelt,Theodore"Teddy";SO
文件scorefile由學生ID、科目號、分數三個欄位組成,由空格隔開:
123456 1 98
212121 1 86
246802 1 89
357913 1 90
123456 2 96
212121 2 88
357913 2 92
123456 3 97
212121 3 96
246802 3 95
357913 3 94
程序應該輸出如下的結果:
Stu-IDName...123Totals:
357913Jefferson,Thomas909294276
246802Lincoln,Abraham"Abe"8995184
212121Roosevelt,Theodore"Teddy"868896270
123456Washington,George989697291
Totals:363276382
源程序如下:
#!/usr/local/bin/perl
#Gradebook-demonstratesI/O,associative
#arrays,sorting,andreportformatting.
#Thisaccommodatesanynumberofexamsandstudents
#andmissingdata.Inputfilesare:
$stufile='stufile';
$scorefile='scorefile';
#Iffileopenssuccessfully,thisevaluatesas"true",andPerl
#doesnotevaluaterestofthe"or""||"
open(NAMES,"<$stufile")||die"Can'topen$stufile$!";
open(SCORES,"<$scorefile")||die"Can'topen$scorefile$!";
#Buildanassociativearrayofstudentinfo
#keyedbystudentnumber
while(){
($stuid,$name,$year)=split(':',$_);
$name{$stuid}=$name;
if(length($name)>$maxnamelength){
$maxnamelength=length($name);
}}
closeNAMES;
#Buildatablefromthetestscores:
while(){
($stuid,$examno,$score)=split;
$score{$stuid,$examno}=$score;
if($examno>$maxexamno){
$maxexamno=$examno;
}}
closeSCORES;
#Printthereportfromaccumulateddata!
printf"%6s%-${maxnamelength}s",
'Stu-ID','Name...';
foreach$examno(1..$maxexamno){
printf"%4d",$examno;
}
printf"%10s ",'Totals:';
#Subroutine"byname"isusedtosortthe%namearray.
#The"sort"functiongivesvariables$aand$bto
#subroutinesitcalls.
#"xcmpy"functionreturns-1ifx #+1ifx>y.SeethePerldocumentationfordetails.
subbyname{$name{$a}cmp$name{$b}}
#OrderstudentIDssothenamesappearalphabetically:
foreach$stuid(sortbynamekeys(%name)){
#Printscoresforastudent,andatotal:
printf"%6d%-${maxnamelength}s",
$stuid,$name{$stuid};
$total=0;
foreach$examno(1..$maxexamno){
printf"%4s",$score{$stuid,$examno};
$total+=$score{$stuid,$examno};
$examtot{$examno}+=$score{$stuid,$examno};
}
printf"%10d ",$total;
}
printf" %6s%${maxnamelength}s","Totals:";
foreach$examno(1..$maxexamno){
printf"%4d",$examtot{$examno};
}
print" ";
exit(0);
[目錄]
--------------------------------------------------------------------------------
正則表達式
正則表達式
正則表達式在 shell、工具程序、Perl 語言中有非常重要的地位。正則表達式通過一些特殊符號表示特定的字元串模式。常見的特殊字元包括:
字元 功能
^ 置於待搜索的字元串之前,匹配行首的字
$ 置於待搜索的字元串之後,匹配行末的字
< 匹配一個字的字頭
> 匹配一個字的字尾
. 匹配任意單個正文字元
[str] 匹配字元串 str 中的任意單個字元
[^str] 匹配不在字元串 str 中的任意單個字元
[a-c] 匹配從 a 到 c 之間的任一字元
* 匹配前一個字元的 0 次或多次出現
忽略特殊字元的特殊含義,將其看作普通字元
擴充的特殊字元:
字元 功能
+ 重複匹配前一項 1 次以上
? 重複匹配前一項 0 次或 1 次
{j} 重複匹配前一項 j 次
{j, } 重複匹配前一項 j 次以上
{, k} 重複匹配前一項最多 k 次
{j, k} 重複匹配前一項 j 到 k 次
s | t 匹配 s 或 t 中的一項
(exp) 將表達式 exp 作為單項處理 (linux知識寶庫)