最簡單的例子 —— 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 [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匹配,則可以使用或():
/catCAT/{print}
在gawk中,有幾個字符有特殊意義。下面列出可以用在gawk格式中的這些字符:
?^表示字段的開始。
例如:
$3~/^b/
如果第三個字段以字符b開始,則匹配。
?$表示字段的結束。
例如:
$3~/b$/
如果第三個字段以字符b結束,則匹配。
?.表示和任何單字符m匹配。
例如:
$3~/i.m/
如果第三個字段有字符i,則匹配。
?表示“或”。
例如:
/catCAT/
和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-lfilesgawk'{x+=$4};END{print"totalbytes:"x}'
此程序會顯示出所有指定的文件的總字節數。
expandfilegawk'{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$filespecsort-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。