bash(詳解)(轉)

post0發表於2007-08-11
bash(詳解)(轉)[@more@]

最簡單的例子 —— 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 不處在同一個檔案系統下時將顯示出特別的優勢。

下面還幾種不常見的用法:

nn>&- 表示將 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 [list]

do

  statments

done

其中 $var 是迴圈控制變數,[list] 是 $var 需要遍歷的一個集合,do/done 對包含了迴圈體,相當於 C 語言中的一對大括號。另外如果do 和 for 被寫在同一行,必須在 do 前面加上 ";"。如: for $var in [list]; 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 [list] 部分,則 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$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#

5@amounts=(10,24,39);

6@parts=('computer','rat',"kbd");

7

8$a=1;$b=2;$c='3';

9@count=($a,$b,$c);

10

11@empty=();

12

13@spare=@parts;

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"Itemsin@amounts=$#amounts ";

44$size=@amounts;print"SizeofAmount=$size ";

45print"Item0in@amounts=$amounts[$[] ";

以下是顯示結果:

@amounts=102439

@parts=computerratkbd

@count=123

@empty=

@spare=computerratkbd

$amounts[0]=10

$amounts[1]=24

$amounts[2]=39

$amounts[3]=

Itemsin@amounts=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

print@lines;#Printthearray

open函式開啟一個檔案以供讀取,其中第一個引數是檔案控制程式碼(filehandle)。檔案控制程式碼用來供Perl在以後指向該檔案。第二個引數指向該檔案的檔名。close函式關閉該檔案。

open函式還可以用來開啟檔案以供寫入和追加,只須分別在檔名之前加上>和>>:

open(INFO,$file);#Openforinput

open(INFO,">$file");#Openforoutput

open(INFO,">>$file");#Openforappending

open(INFO,"

另外,如果你希望輸出內容到一個已經開啟供寫入的檔案中,你可以使用帶有額外引數的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,"open(SCORES,"#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知識寶庫

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/8225414/viewspace-944896/,如需轉載,請註明出處,否則將追究法律責任。

相關文章