Note for Shell



Shell:---------------------------------------------------------------------------------------------------------------
1、{} 大括號:
用法一:通配符擴展 
eg: ls my_{finger,toe}s
這條命令相當於如下命令的組合:
ls my_fingers my_toes
eg: mkdir {userA,userB,userC}-{home,bin,data}
我們將得到 userA-home, userA-bin, userA-data, userB-home, userB-bin,userB-data,userC-home, userC-bin, userC-data,這幾個目錄
用法二:可用於語句塊的構造,語句之間用回車隔開。如果你想在某些使用單個語句的地方(比如在AND或OR列表中)使用多條語句,你可以把它們括在花括號{}中來構造一個語句塊。
eg: 
{
grep -v "$cdcatnum" $strack_file > $temp_file
cat $temp_file > $strack_file
echo
cat -n file1
} (注:以上大括號中的四句命令夠成了一個語句塊)
用法三:參數擴展
${name:-default} 使用一個默認值(一般是空值)來代替那些空的或者沒有賦值的變量name;
${name:=default}使用指定值來代替空的或者沒有賦值的變量name;
${name:?message}如果變量爲空或者未賦值,那麼就會顯示出錯誤信息並中止腳本的執行同時返回退出碼1。
${#name} 給出name的長度
${name%word} 從name的尾部開始刪除與word匹配的最小部分,然後返回剩餘部分
${name%%word} 從name的尾部開始刪除與word匹配的最長部分,然後返回剩餘部分
${name#word} 從name的頭部開始刪除與word匹配的最小部分,然後返回剩餘部分
${name##word} 從name的頭部開始刪除與word匹配的最長部分,然後返回剩餘部分
(注,name爲變量名,word爲要匹配的字符串)
用法三在處理字符串和未知變量時,是很有用的。
2、[] 中括號: 
用法一:通配符擴展:
允許匹配方括號中任何一個單個字符
eg: ls /[eh][to][cm]*
相當於執行 ls /etc /home(若有/eom目錄,就相當於會執行ls /etc /home /eom)
注:在mkdir命令下不能擴展
用法二:用於條件判斷符號:
[]符號可理解爲指向test命令的一個軟鏈接,所以其用法可完全參照test,將test位置替換爲[便可。
eg: if [ "$?" != 0 ] 等價於 if test "$?" != 0
then echo "Executes error"
3、`command` 反引號:`command`與$(command)的含義相同,都是返回當前執行命令的結果 
eg: #!/bin/sh
for file in $(ls f*.sh);do
lpr $file
done
exit 0
該例實現了擴展f*.sh給出所有匹配模式的文件的名字。
4、'string' 單引號 和 "string" 雙引號 
雙引號:如果想在定義的變量中加入空格,就必須使用單引號或雙引號,
單、雙引號的區別在於雙引號轉義特殊字符而單引號不轉義特殊字符
eg: $ heyyou=home
$ echo '$heyyou'
$ $heyyou ($沒有轉義)
eg: $ heyyou=home
$ echo "$heyyou"
$ home (很明顯,$轉義了輸出了heyyou變量的值)
5、$# 它的作用是告訴你引用變量的總數量是多少; 
$$ 它的作用是告訴你shell腳本的進程號;
$* 以一個單字符串顯示所有的腳本傳遞的參數。等價於$1 $2 $3.......;
$@ 與$*基本類似(參見序號7),但在數組賦值時有些不同;
$? 前一個命令的退出碼;
$- 顯示shell使用的當前選項;
$! 最後一個後臺運行的進程ID號。
6、$((...))語法:對括號內的表達式求值 
eg:
#!/bin/sh
x=0
hile [ "$x" -ne 10 ];do
echo $x
x=$(($x+1))
done
exit 0
7、shell中幾種特殊的參數變量的引用 
$1、$2、$3……${10}、${11}、${12}…… :表示腳本傳入的的各個參數,注意當需表示兩位數以後的參數時數字要用花括號括起。
$@ 列出所有的參數,各參數用空格隔開
$*: 列出所有的參數,各參數用環境變量IFS的第一個字符隔開
8、命令列表: 
AND列表 statement1 && statement2 && statement3 && …:只有在前面所有的命令都執行成功的情況下才執行後一條命令
OR列表 statement1 || statement2 || statement3 || …:允許執行一系列命令直到有一條命令成功爲止,其後所有命令將不再被執行
eg:#!/bin/sh
touch file_one
rm -f file_two
if [ -f file_one ] && echo "hello" && [ -f file_two ] && echo " there"
then
echo "in if"
else
echo "in else"
fi
exit 0
上例的輸出爲:
hello
in else
關於AND列表與OR列表,在邏輯判斷中很使用,下面就舉一個其最常用的例子:
[ condition ] && command for true || command for false:
當條件爲真時,執行commandfor true ,當條件爲假時,執行command for false
9、: 冒號:內建空指令,返回值爲0 
eg: $ :
$ echo $?
$ 0
while: (該語句結構可實現一個無限循環)
10、; 分號: 在 shell 中,擔任"連續指令"功能的符號就是"分號" 
eg:cd ~/backup ; mkdir startup ; cp ~/.* startup/.
11、# 井號:表示符號後面的是註解文字,不會被執行; 
* 匹配文件名中的任何字符,包括字符串;
? 匹配文件名中的任何單個字符。
~ 代表使用者的 home 目錄
12、\ 倒斜線:放在指令前,有取消 aliases(別名) 的作用;放在特殊符號前,則該特殊符號的作用消失;放在指令 
的最末端,表示指令連接下一行(使得回車符無效,只起換行作用)
14、! 感嘆號:通常它代表反邏輯的作用,譬如條件偵測中,用 != 來代表"不等於" 
15、** 次方運算:兩個星號在運算時代表 "次方" 的意思 
eg:let "sus=2**3"
echo "sus = $sus"
$ sus = 8 -


七種文件類型
d            目錄                                                       l             符號鏈接


s             套接字文件                                           b            塊設備文件


c            字符設備文件                                       p            命名管道文件


-             普通文件


正則表達式
從一個文件或命令輸出中抽取或過濾文本時。可使用正則表達式(RE),正則表達式是一些特殊或不很特殊的字符串模式的集合。


       基本的元字符集:


       ^                   只匹配行首。


       $                   只匹配行尾。


       *                   一個單字符後緊跟*,匹配0個或多個此單字符。


       []                   匹配[]內字符,可以是一個單字符,也可以是字符序列。可以使


              用-來表示[]內範圍,如[1-5]等價於[1,2,3,4,5]。


\                    屏蔽一個元字符的特殊含義,如\$表示字符$,而不表示匹配行


                    尾。


       .                 匹配任意單字符。


       pattern\{n\}   匹配pattern出現的次數n


       pattern\{n,\}m匹配pattern出現的次數,但表示次數最少爲n


       pattern\{n,m\} 匹配pattern出現的次數在n與m之間(n,m爲0-255)


幾個常見的例子:


       顯示可執行的文件:ls –l | grep …x...x..x


       只顯示文件夾:ls –l | grep  ^d


       匹配所有的空行:^$


       匹配所有的單詞:[A-Z a-z]*


       匹配任一非字母型字符:[^A-Z a-z]


       包含八個字符的行:^……..$(8個.)


字符類描述
以下是可用字符類的相當完整的列表:


[:alnum:] 字母數字 [a-z A-Z 0-9] 


[:alpha:] 字母 [a-z A-Z] 


[:blank:] 空格或製表鍵 


[:cntrl:] 任何控制字符 


[:digit:] 數字 [0-9] 


[:graph:] 任何可視字符(無空格) 


[:lower:] 小寫 [a-z] 


[:print:] 非控制字符 


[:punct:] 標點字符 


[:space:] 空格 


[:upper:] 大寫 [A-Z] 


[:xdigit:] 十六進制數字 [0-9 a-f A-F] 


儘可能使用字符類是很有利的,因爲它們可以更好地適應非英語 locale(包括某些必需的重音字符等等).


shell的引號類型
shell共有四種引用類型:


       “ ”          雙引號


       ‘ ’           單引號


` `         反引號


\            反斜線


l        “ ” 可引用除$、` 、\ 、外的任意字符或字符串,“ ”中的變量能夠正常顯示變量值。


l        ‘ ’與“ ”類似,不同在於shell會忽略任何的引用值。


              例如: GIRL=‘girl’ 


                        echo “The ‘$GIRL’ did well”


              則打印:The ‘girl’ did well


l        ` `用於設置系統命令的輸出到變量,shell會將` `中的內容作爲一個系統命令並執行質。


              例如:echo `date` 則打印當前的系統時間。


l        \ 用來屏蔽特殊含義的字符:&  *  +  ^  $  `  “  |  ?


例如:expr 12 \* 12 將輸出144


變量設置時的不同模式:
valiable_name=value           設置實際值到 variable_name中


valiable_name+value           如果設置了variable_name,則重設其值


valiable_name:?value           如果未設置variable_name,則先顯示未定義用戶錯誤信息


valiable_name?value           如果未設置variable_name,則顯示系統錯誤信息


valiable_name:=value   如果未設置variable_name,則設置其值


valiable_name-value            同上,但取值並不設置到variable_name


條件測試
test命令用於測試字符串、文件狀態和數字,expr測試和執行數值輸出。


Test格式:test condition 或 [ condition ](需要特別注意的是condition的兩邊都要有一個空格,否則會報錯),test命令返回0表示成功。


l        下面將分別描述test的三種測試:


n        文件狀態測試(常用的)


-d           測試是否文件夾


-f            測試是否一般文件


-L          測試是否鏈接文件


-r           測試文件是否可讀


-w         測試文件是否可寫


-x           測試文件是否可執行


-s           測試文件是否非空


n        字符串測試


五種格式: test  “string”


                            test  string_operator  “string”


                            test  “string”  string_operator  “string”


                            [ string_operator  “string” ]


                            [ “string”  string_operator  “string” ]


其中string_operator可以爲:       =     兩字符串相等


                                                               !=    兩字符串不等


                                                               -z   空串


                                                               -n   非空串


n        數值測試


兩種格式: “number”  number_operator  “number”


                            [ “number”  number_operator  “number” ]


其中:number_operator 可以爲:-eq  、-ne、-gt、-lt、-ge


例如:  NUMBER=130


                     [ “990”  –le  “995”  –a  “NUMBER”  -gt  “133” ]


                     (其中-a表示前後結果相“與”)


l        expr命令一般用於整數值,但也可以用於字符串。


n        格式:  expr srgument operator operator argument


例如:  expr 10 + 10


              expr 10 ^ 2 (10的平方)


              expr $value + 10


n        增量計數――expr在循環中最基本的用法


例如:  LOOP=0


              LOOP=`expr $LOOP + 1`


n        模式匹配:通過指定的冒號選項計算字符串中的字符數


例如:  value=account.doc


              expr $value : `\(.*\).doc`


              輸出 account


命令執行順序
&&               成功執行一個命令後再執行下一個


||                    一個命令執行失敗後再執行另一個命令


( )                  在當前shell中執行一組命令(格式:(命令1;命令2; ……))


{ }                同( )


       例如:  comet mouth_end || ( echo “hello” | mail dave ;exit )


             如果沒有( ),則shell將直接執行最後一個命令(exit)


腳本調試
最有用的調試腳本的工具是echo命令,可以隨時打印有關變量或操作的信息,以幫助定位錯誤。也可使用打印最後狀態($?) 命令來判斷命令是否成功,這時要注意的是要在執行完要測試的命令後立即輸出$?,否則$?將會改變。


Set命令也可以用來輔助腳本測試:


Set –n           讀命令但是不執行


Set –v           顯示讀取的所有的行


Set –x           顯示所有的命令及其參數


(要關閉set選項,只要把-換成+就可以了,這裏有點特殊,要注意一下)


一些常用的小trick
打印一些頭信息
command  <<  dilimiter


……


……


dilimiter


以分界符號dilimiter中的內容作爲命令的標準輸入


       常用在echo命令中,這樣就避免了沒輸出一行就要使用一個echo命令,同時,輸出格式的調整也相應變得簡單了。


       例如:  echo << something_message


************************************************


                                         hello, welcome to use my shell script


************************************************


                something_message


將在屏幕上輸出:


************************************************


                                         hello, welcome to use my shell script


************************************************


一、利用<<的分解符號性質還可以自動選擇菜單或實現自動的ftp傳輸


也就是利用分解符號的性質自動選擇菜單。


例如: ./menu_choose >>output_file 2>&1 <<Choose


             2


             3


             Y


             Choose


             則自動在執行腳本的過程中一步步作出選擇:2,3,Y


<<這種性質決定了它是理想的訪問數據庫的有用工具,可以用它來輸入面對數據庫提示時所作的各種選擇。


創建一個長度爲0的空文件
執行 > file_name 命令或 touch file_name 命令。


一些常用的shell變量
$#          傳遞到腳本的參數個數


$*          以一個單字符串顯示所有向腳本傳遞的參數(可大於9個)


$$          腳本運行的當前進程的ID號


$!           後臺運行的最後一個進程的ID號


$@        與$#相同,但使用時加引號,並在引號中返回每個參數


$-           顯示shell使用的當前選項


$?                 顯示最後命令的退出狀態,0表示無錯誤(這個變量也常常用來打印輸出,在腳本調試時標記某個shell命令或某個函數是否正確執行,但是要注意,$?記載的是最近的函數或命令的退出狀態,因此打印時應該立即打印以獲得正確的信息)


$0的使用
在變量中有一種位置變量$n,用來存放函數調用或腳本執行時傳入的參數,其中$0表示函數名或腳本名,需要注意的是,這時的腳本名傳遞的是包含全路徑的腳本名。從$1-$9表示傳入的第一到第九個參數,這樣的參數表示不能多於九個,如果多於九個,可以使用下面將要提到的shift指令來讀取。


因爲$0存放函數名或腳本名,因此我們可以通過echo $0來輸出調用信息,但是,由於存放的是全路徑名,我們可以利用一個shell命令來得到腳本名,basename $0 將得到$0中名字的部分,而與之相反的,dirname $0將得到$0中路徑的部分。


Shift的運用
用head或tail指令指定查閱的行數
例如:查閱文件前20行:  head –20 file_name


             查閱文件後10行: tail –10 file_name


awk使用規則
awk 是一種很棒的語言。awk 適合於文本處理和報表生成,它還有許多精心設計的特性,允許進行需要特殊技巧程序設計。與某些語言不同,awk 的語法較爲常見。它借鑑了某些語言的一些精華部分,如 C 語言、python 和 bash(雖然在技術上,awk 比 python 和 bash 早創建)。awk 是那種一旦學會了就會成爲您戰略編碼庫的主要部分的語言。


第一個 awk
讓我們繼續,開始使用 awk,以瞭解其工作原理。在命令行中輸入以下命令:


$ awk '{ print }' /etc/passwd


您將會見到 /etc/passwd 文件的內容出現在眼前。現在,解釋 awk 做了些什麼。調用 awk 時,我們指定 /etc/passwd 作爲輸入文件。執行 awk 時,它依次對 /etc/passwd 中的每一行執行 print 命令。所有輸出都發送到 stdout,所得到的結果與與執行catting /etc/passwd完全相同。


現在,解釋 { print } 代碼塊。在 awk 中,花括號用於將幾塊代碼組合到一起,這一點類似於 C 語言。在代碼塊中只有一條 print 命令。在 awk 中,如果只出現 print 命令,那麼將打印當前行的全部內容。


       這裏是另一個 awk 示例,它的作用與上例完全相同:


              $ awk '{ print $0 }' /etc/passwd


在 awk 中,$0 變量表示整個當前行,所以 print 和 print $0 的作用完全一樣。


如果您願意,可以創建一個 awk 程序,讓它輸出與輸入數據完全無關的數據。以下是一個示例:


$ awk '{ print "" }' /etc/passwd


只要將 "" 字符串傳遞給 print 命令,它就會打印空白行。如果測試該腳本,將會發現對於 /etc/passwd 文件中的每一行,awk 都輸出一個空白行。再次說明, awk 對輸入文件中的每一行都執行這個腳本。以下是另一個示例:


$ awk '{ print "hiya" }' /etc/passwd


運行這個腳本將在您的屏幕上寫滿 hiya。:)


多個字段
awk 非常善於處理分成多個邏輯字段的文本,而且讓您可以毫不費力地引用 awk 腳本中每個獨立的字段。以下腳本將打印出您的系統上所有用戶帳戶的列表:


$ awk -F":" '{ print $1 }' /etc/passwd


上例中,在調用 awk 時,使用 -F 選項來指定 ":" 作爲字段分隔符。awk 處理 print $1 命令時,它會打印出在輸入文件中每一行中出現的第一個字段。以下是另一個示例:


$ awk -F":" '{ print $1 $3 }' /etc/passwd


以下是該腳本輸出的摘錄:


halt7


operator11


root0


shutdown6


sync5


bin1


....etc.


如您所見,awk 打印出 /etc/passwd 文件的第一和第三個字段,它們正好分別是用戶名和用戶標識字段。現在,當腳本運行時,它並不理想 -- 在兩個輸出字段之間沒有空格!如果習慣於使用 bash 或 python 進行編程,那麼您會指望 print $1 $3 命令在兩個字段之間插入空格。然而,當兩個字符串在 awk 程序中彼此相鄰時,awk 會連接它們但不在它們之間添加空格。以下命令會在這兩個字段中插入空格:


$ awk -F":" '{ print $1 " " $3 }' /etc/passwd


以這種方式調用 print 時,它將連接 $1、" " 和 $3,創建可讀的輸出。當然,如果需要的話,我們還可以插入一些文本標籤:


$ awk -F":" '{ print "username: " $1 "\t\tuid:" $3" }' /etc/passwd


這將產生以下輸出:


username: halt          uid:7


username: operator      uid:11


username: root          uid:0


username: shutdown      uid:6


username: sync          uid:5


username: bin           uid:1


....etc.


外部腳本
將腳本作爲命令行自變量傳遞給 awk 對於小的單行程序來說是非常簡單的,而對於多行程序,它就比較複雜。您肯定想要在外部文件中撰寫腳本。然後可以向 awk 傳遞 -f 選項,以向它提供此腳本文件:


$ awk -f myscript.awk myfile.in


將腳本放入文本文件還可以讓您使用附加 awk 功能。例如,這個多行腳本與前面的單行腳本的作用相同,它們都打印出 /etc/passwd 中每一行的第一個字段:


BEGIN {


                 FS=":"


}


{ print $1 }


這兩個方法的差別在於如何設置字段分隔符。在這個腳本中,字段分隔符在代碼自身中指定(通過設置 FS 變量),而在前一個示例中,通過在命令行上向 awk 傳遞 -F":" 選項來設置 FS。通常,最好在腳本自身中設置字段分隔符,只是因爲這表示您可以少輸入一個命令行自變量。我們將在本文的後面詳細討論 FS 變量。


BEGIN 和 END 塊
通常,對於每個輸入行,awk 都會執行每個腳本代碼塊一次。然而,在許多編程情況中,可能需要在 awk 開始處理輸入文件中的文本之前執行初始化代碼。對於這種情況,awk 允許您定義一個 BEGIN 塊。我們在前一個示例中使用了 BEGIN 塊。因爲 awk 在開始處理輸入文件之前會執行 BEGIN 塊,因此它是初始化 FS(字段分隔符)變量、打印頁眉或初始化其它在程序中以後會引用的全局變量的極佳位置。


awk 還提供了另一個特殊塊,叫作 END 塊。awk 在處理了輸入文件中的所有行之後執行這個塊。通常,END 塊用於執行最終計算或打印應該出現在輸出流結尾的摘要信息。


規則表達式和塊
awk 允許使用規則表達式,根據規則表達式是否匹配當前行來選擇執行獨立代碼塊。以下示例腳本只輸出包含字符序列 foo 的那些行:


/foo/ { print }


當然,可以使用更復雜的規則表達式。以下腳本將只打印包含浮點數的行:


/[0-9]+\.[0-9]*/ { print }


還有許多其它方法可以選擇執行代碼塊。我們可以將任意一種布爾表達式放在一個代碼塊之前,以控制何時執行某特定塊。僅當對前面的布爾表達式求值爲真時,awk 才執行代碼塊。以下示例腳本輸出將輸出其第一個字段等於 fred 的所有行中的第三個字段。如果當前行的第一個字段不等於 fred,awk 將繼續處理文件而不對當前行執行 print 語句:


$1 == "fred" { print $3 }


awk 提供了完整的比較運算符集合,包括 "=="、"<"、">"、"<="、">=" 和 "!="。另外,awk 還提供了 "~" 和 "!~" 運算符,它們分別表示“匹配”和“不匹配”。它們的用法是在運算符左邊指定變量,在右邊指定規則表達式。如果某一行的第五個字段包含字符序列 root,那麼以下示例將只打印這一行中的第三個字段:


$5 ~ /root/ { print $3 }


條件語句
awk 還提供了非常好的類似於 C 語言的 if 語句。如果您願意,可以使用 if 語句重寫前一個腳本:





                 if ( $5 ~ /root/ ) { 


                    print $3 


                 }


}


這兩個腳本的功能完全一樣。第一個示例中,布爾表達式放在代碼塊外面。而在第二個示例中,將對每一個輸入行執行代碼塊,而且我們使用 if 語句來選擇執行 print 命令。這兩個方法都可以使用,可以選擇最適合腳本其它部分的一種方法。


以下是更復雜的 awk if 語句示例。可以看到,儘管使用了複雜、嵌套的條件語句,if 語句看上去仍與相應的 C 語言 if 語句一樣:


{


                 if ( $1 == "foo" ) {


                    if ( $2 == "foo" ) {


                       print "uno"


                    } else {


                       print "one"


                    }


                 } else if ($1 == "bar" ) {


                    print "two"


                 } else {


                    print "three"


                 }


}


使用 if 語句還可以將代碼:


! /matchme/ { print $1 $3 $4 }


轉換成:


{   


                 if ( $0 !~ /matchme/ ) {


                    print $1 $3 $4


                 }


}


這兩個腳本都只輸出不包含 matchme 字符序列的那些行。此外,還可以選擇最適合您的代碼的方法。它們的功能完全相同。


awk 還允許使用布爾運算符 "||"(邏輯與)和 "&&"(邏輯或),以便創建更復雜的布爾表達式:


( $1 == "foo" ) && ( $2 == "bar" ) { print } 


這個示例只打印第一個字段等於 foo 且第二個字段等於 bar 的那些行。


數值變量
至今,我們不是打印字符串、整行就是特定字段。然而,awk 還允許我們執行整數和浮點運算。通過使用數學表達式,可以很方便地編寫計算文件中空白行數量的腳本。以下就是這樣一個腳本:


              BEGIN   { x=0 }


/^$/    { x=x+1 }


END     { print "I found " x " blank lines. :}" }


在 BEGIN 塊中,將整數變量 x 初始化成零。然後,awk 每次遇到空白行時,awk 將執行 x=x+1 語句,遞增 x。處理完所有行之後,執行 END 塊,awk 將打印出最終摘要,指出它找到的空白行數量。


字符串化變量
awk 的優點之一就是“簡單和字符串化”。我認爲 awk 變量“字符串化”是因爲所有 awk 變量在內部都是按字符串形式存儲的。同時,awk 變量是“簡單的”,因爲可以對它執行數學操作,且只要變量包含有效數字字符串,awk 會自動處理字符串到數字的轉換步驟。要理解我的觀點,請研究以下這個示例:


x="1.01"


# We just set x to contain the *string* "1.01"


x=x+1


# We just added one to a *string* 


print x


# Incidentally, these are comments :)


awk 將輸出:


              2.01


有趣吧!雖然將字符串值 1.01 賦值給變量 x,我們仍然可以對它加一。但在 bash 和 python 中卻不能這樣做。首先,bash 不支持浮點運算。而且,如果 bash 有“字符串化”變量,它們並不“簡單”;要執行任何數學操作,bash 要求我們將數字放到醜陋的 $( ) ) 結構中。如果使用 python,則必須在對 1.01 字符串執行任何數學運算之前,將它轉換成浮點值。雖然這並不困難,但它仍是附加的步驟。如果使用 awk,它是全自動的,而那會使我們的代碼又好又整潔。如果想要對每個輸入行的第一個字段乘方並加一,可以使用以下腳本:


{ print ($1^2)+1 }


如果做一個小實驗,就可以發現如果某個特定變量不包含有效數字,awk 在對數學表達式求值時會將該變量當作數字零處理。


衆多運算符
awk 的另一個優點是它有完整的數學運算符集合。除了標準的加、減、乘、除,awk 還允許使用前面演示過的指數運算符 "^"、模(餘數)運算符 "%" 和其它許多從 C 語言中借入的易於使用的賦值操作符。


這些運算符包括前後加減(i++、--foo)、加/減/乘/除賦值運算符( a+=3、b*=2、c/=2.2、d-=6.2)。不僅如此 -- 我們還有易於使用的模/指數賦值運算符(a^=2、b%=4)。


字段分隔符
awk 有它自己的特殊變量集合。其中一些允許調整 awk 的運行方式,而其它變量可以被讀取以收集關於輸入的有用信息。我們已經接觸過這些特殊變量中的一個,FS。前面已經提到過,這個變量讓您可以設置 awk 要查找的字段之間的字符序列。我們使用 /etc/passwd 作爲輸入時,將 FS 設置成 ":"。當這樣做有問題時,我們還可以更靈活地使用 FS。


FS 值並沒有被限制爲單一字符;可以通過指定任意長度的字符模式,將它設置成規則表達式。如果正在處理由一個或多個 tab 分隔的字段,您可能希望按以下方式設置 FS:


FS="\t+"


以上示例中,我們使用特殊 "+" 規則表達式字符,它表示“一個或多個前一字符”。


如果字段由空格分隔(一個或多個空格或 tab),您可能想要將 FS 設置成以下規則表達式:


FS="[[:space:]+]"


這個賦值表達式也有問題,它並非必要。爲什麼?因爲缺省情況下,FS 設置成單一空格字符,awk 將這解釋成表示“一個或多個空格或 tab”。在這個特殊示例中,缺省 FS 設置恰恰是您最想要的!


複雜的規則表達式也不成問題。即使您的記錄由單詞 "foo" 分隔,後面跟着三個數字,以下規則表達式仍允許對數據進行正確的分析:


FS="foo[0-9][0-9][0-9]"


字段數量
接着我們要討論的兩個變量通常並不是需要賦值的,而是用來讀取以獲取關於輸入的有用信息。第一個是 NF 變量,也叫做“字段數量”變量。awk 會自動將該變量設置成當前記錄中的字段數量。可以使用 NF 變量來只顯示某些輸入行:


NF == 3 { print "this particular record has three fields: " $0 }


當然,也可以在條件語句中使用 NF 變量,如下:


{   


                 if ( NF > 2 ) {


                    print $1 " " $2 ":" $3 


                 }


}


記錄號
記錄號 (NR) 是另一個方便的變量。它始終包含當前記錄的編號(awk 將第一個記錄算作記錄號 1)。迄今爲止,我們已經處理了每一行包含一個記錄的輸入文件。對於這些情況,NR 還會告訴您當前行號。然而,當我們在本系列以後部分中開始處理多行記錄時,就不會再有這種情況,所以要注意!可以象使用 NF 變量一樣使用 NR 來只打印某些輸入行:


(NR < 10 ) || (NR > 100) { print "We are on record number 1-9 or 101+" }


另一個示例:


{


                 #skip header


                 if ( NR > 10 ) {


                    print "ok, now for the real information!"


                 }


}


awk 提供了適合各種用途的附加變量。我們將在以後的文章中討論這些變量。


多行記錄
awk 是一種用於讀取和處理結構化數據(如系統的 /etc/passwd 文件)的極佳工具。/etc/passwd 是 UNIX 用戶數據庫,並且是用冒號定界的文本文件,它包含許多重要信息,包括所有現有用戶帳戶和用戶標識,以及其它信息。在我的前一篇文章中,我演示了 awk 如何輕鬆地分析這個文件。我們只須將 FS(字段分隔符)變量設置成 ":"。


正確設置了 FS 變量之後,就可以將 awk 配置成分析幾乎任何類型的結構化數據,只要這些數據是每行一個記錄。然而,如果要分析佔據多行的記錄,僅僅依靠設置 FS 是不夠的。在這些情況下,我們還需要修改 RS 記錄分隔符變量。RS 變量告訴 awk 當前記錄什麼時候結束,新記錄什麼時候開始。


譬如,讓我們討論一下如何完成處理“聯邦證人保護計劃”所涉及人員的地址列表的任務: 


Jimmy the Weasel


100 Pleasant Drive


San Francisco, CA 12345


Big Tony


200 Incognito Ave.


Suburbia, WA 67890


理論上,我們希望 awk 將每 3 行看作是一個獨立的記錄,而不是三個獨立的記錄。如果 awk 將地址的第一行看作是第一個字段 ($1),街道地址看作是第二個字段 ($2),城市、州和郵政編碼看作是第三個字段 $3,那麼這個代碼就會變得很簡單。以下就是我們想要得到的代碼: 


BEGIN {


                     FS="\n"


                     RS=""


}


在上面這段代碼中,將 FS 設置成 "\n" 告訴 awk 每個字段都佔據一行。通過將 RS 設置成 "",還會告訴 awk 每個地址記錄都由空白行分隔。一旦 awk 知道是如何格式化輸入的,它就可以爲我們執行所有分析工作,腳本的其餘部分很簡單。讓我們研究一個完整的腳本,它將分析這個地址列表,並將每個記錄打印在一行上,用逗號分隔每個字段。


address.awk BEGIN {


                     FS="\n"


                     RS=""


}


{


                     print $1 ", " $2 ", " $3


}


如果這個腳本保存爲 address.awk,地址數據存儲在文件 address.txt 中,可以通過輸入 "awk -f address.awk address.txt" 來執行這個腳本。此代碼將產生以下輸出: 


Jimmy the Weasel, 100 Pleasant Drive, San Francisco, CA 12345


Big Tony, 200 Incognito Ave., Suburbia, WA 67890


OFS 和 ORS
在 address.awk 的 print 語句中,可以看到 awk 會連接(合併)一行中彼此相鄰的字符串。我們使用此功能在同一行上的三個字段之間插入一個逗號和空格 (", ")。這個方法雖然有用,但比較難看。與其在字段間插入 ", " 字符串,倒不如讓通過設置一個特殊 awk 變量 OFS,讓 awk 完成這件事。請參考下面這個代碼片斷。 


print "Hello", "there", "Jim!"


這行代碼中的逗號並不是實際文字字符串的一部分。事實上,它們告訴 awk "Hello"、"there" 和 "Jim!" 是單獨的字段,並且應該在每個字符串之間打印 OFS 變量。缺省情況下,awk 產生以下輸出: 


Hello there Jim!


這是缺省情況下的輸出結果,OFS 被設置成 " ",單個空格。不過,我們可以方便地重新定義 OFS,這樣 awk 將插入我們中意的字段分隔符。以下是原始 address.awk 程序的修訂版,它使用 OFS 來輸出那些中間的 ", " 字符串:


address.awk 的修訂版 BEGIN {


                     FS="\n"


                     RS=""


                     OFS=", "


}


{


                     print $1, $2, $3


}


       awk 還有一個特殊變量 ORS,全稱是“輸出記錄分隔符”。通過設置缺省爲換行 ("\n") 的 OFS,我們可以控制在 print 語句結尾自動打印的字符。缺省 ORS 值會使 awk 在新行中輸出每個新的 print 語句。如果想使輸出的間隔翻倍,可以將 ORS 設置成 "\n\n"。或者,如果想要用單個空格分隔記錄(而不換行),將 ORS 設置成 " "。


將多行轉換成用 tab 分隔的格式
假設我們編寫了一個腳本,它將地址列表轉換成每個記錄一行,且用 tab 定界的格式,以便導入電子表格。使用稍加修改的 address.awk 之後,就可以清楚地看到這個程序只適合於三行的地址。如果 awk 遇到以下地址,將丟掉第四行,並且不打印該行:


Cousin Vinnie


Vinnie's Auto Shop


300 City Alley


Sosueme, OR 76543


要處理這種情況,代碼最好考慮每個字段的記錄數量,並依次打印每個記錄。現在,代碼只打印地址的前三個字段。以下就是我們想要的一些代碼:


適合具有任意多字段的地址的 address.awk 版本 BEGIN { 


                 FS="\n" 


                 RS="" 


                 ORS="" 





             {  


             x=1 


             while ( x<NF ) { 


print $x "\t" 


              x++ 


             } 


             print $NF "\n" 





首先,將字段分隔符 FS 設置成 "\n",將記錄分隔符 RS 設置成 "",這樣 awk 可以象以前一樣正確分析多行地址。然後,將輸出記錄分隔符 ORS 設置成 "",它將使 print 語句在每個調用結尾不輸出新行。這意味着如果希望任何文本從新的一行開始,那麼需要明確寫入 print "\n"。


在主代碼塊中,創建了一個變量 x 來存儲正在處理的當前字段的編號。起初,它被設置成 1。然後,我們使用 while 循環(一種 awk 循環結構,等同於 C 語言中的 while 循環),對於所有記錄(最後一個記錄除外)重複打印記錄和 tab 字符。最後,打印最後一個記錄和換行;此外,由於將 ORS 設置成 "",print 將不輸出換行。程序輸出如下,這正是我們所期望的(不算漂亮,但用 tab 定界,以便於導入電子表格):


Jimmy the Weasel        100 Pleasant Drive      San Francisco, CA 12345 


Big Tony        200 Incognito Ave.      Suburbia, WA 67890


Cousin Vinnie   Vinnie's Auto Shop      300 City Alley  Sosueme, OR 76543


循環結構
我們已經看到了 awk 的 while 循環結構,它等同於相應的 C 語言 while 循環。awk 還有 "do...while" 循環,它在代碼塊結尾處對條件求值,而不象標準 while 循環那樣在開始處求值。它類似於其它語言中的 "repeat...until" 循環。以下是一個示例:


do...while 示例 {


                     count=1


                     do {


                            print "I get printed at least once no matter what" 


                     } while ( count != 1 )


}


與一般的 while 循環不同,由於在代碼塊之後對條件求值,"do...while" 循環永遠都至少執行一次。換句話說,當第一次遇到普通 while 循環時,如果條件爲假,將永遠不執行該循環。


for 循環
awk 允許創建 for 循環,它就象 while 循環,也等同於 C 語言的 for 循環:


for ( initial assignment; comparison; increment ) {


                     code block


}


以下是一個簡短示例: 


for ( x = 1; x <= 4; x++ ) {


                     print "iteration",x


}


此段代碼將打印: 


iteration 1


iteration 2


iteration 3


iteration 4


break 和 continue
此外,如同 C 語言一樣,awk 提供了 break 和 continue 語句。使用這些語句可以更好地控制 awk 的循環結構。以下是迫切需要 break 語句的代碼片斷:


while 死循環 while (1) {


                     print "forever and ever..."


}


因爲 1 永遠代表是真,這個 while 循環將永遠運行下去。以下是一個只執行十次的循環:


break 語句示例 x=1


while(1) {


                     print "iteration",x


                     if ( x == 10 ) {


                     break


                     }


                     x++


}


這裏,break 語句用於“逃出”最深層的循環。"break" 使循環立即終止,並繼續執行循環代碼塊後面的語句。


continue 語句補充了 break,其作用如下:


x=1


while (1) {


                     if ( x == 4 ) {


                            x++


                            continue


                     }


                     print "iteration",x


                     if ( x > 20 ) {


                            break


                     }


                     x++


}


這段代碼打印 "iteration 1" 到 "iteration 21","iteration 4" 除外。如果迭代等於 4,則增加 x 並調用 continue 語句,該語句立即使 awk 開始執行下一個循環迭代,而不執行代碼塊的其餘部分。如同 break 一樣,continue 語句適合各種 awk 迭代循環。在 for 循環主體中使用時,continue 將使循環控制變量自動增加。以下是一個等價循環: 


for ( x=1; x<=21; x++ ) {


                     if ( x == 4 ) {


                            continue


                     }


                     print "iteration",x


}


在 while 循環中時,在調用 continue 之前沒有必要增加 x,因爲 for 循環會自動增加 x。 


數組


如果您知道 awk 可以使用數組,您一定會感到高興。然而,在 awk 中,數組下標通常從 1 開始,而不是 0: 


myarray[1]="jim"


myarray[2]=456


awk 遇到第一個賦值語句時,它將創建 myarray,並將元素 myarray[1] 設置成 "jim"。執行了第二個賦值語句後,數組就有兩個元素了。 


數組迭代


定義之後,awk 有一個便利的機制來迭代數組元素,如下所示: 


for ( x in myarray ) {


                     print myarray[x]


}


這段代碼將打印數組 myarray 中的每一個元素。當對於 for 使用這種特殊的 "in" 形式時,awk 將 myarray 的每個現有下標依次賦值給 x(循環控制變量),每次賦值以後都循環一次循環代碼。雖然這是一個非常方便的 awk 功能,但它有一個缺點 -- 當 awk 在數組下標之間輪轉時,它不會依照任何特定的順序。那就意味着我們不能知道以上代碼的輸出是: 


jim


456


還是:


456


jim


套用 Forrest Gump 的話來說,迭代數組內容就像一盒巧克力 -- 您永遠不知道將會得到什麼。因此有必要使 awk 數組“字符串化”,我們現在就來研究這個問題。


數組下標字符串化
在我的前一篇文章中,我演示了 awk 實際上以字符串格式來存儲數字值。雖然 awk 要執行必要的轉換來完成這項工作,但它卻可以使用某些看起來很奇怪的代碼: 


a="1"


b="2"


c=a+b+3


執行了這段代碼後,c 等於 6。由於 awk 是“字符串化”的,添加字符串 "1" 和 "2" 在功能上並不比添加數字 1 和 2 難。這兩種情況下,awk 都可以成功執行運算。awk 的“字符串化”性質非常可愛 -- 您可能想要知道如果使用數組的字符串下標會發生什麼情況。例如,使用以下代碼: 


myarr["1"]="Mr. Whipple"


print myarr["1"]


可以預料,這段代碼將打印 "Mr. Whipple"。但如果去掉第二個 "1" 下標中的引號,情況又會怎樣呢? 


myarr["1"]="Mr. Whipple"


print myarr[1]


猜想這個代碼片斷的結果比較難。awk 將 myarr["1"] 和 myarr[1] 看作數組的兩個獨立元素,還是它們是指同一個元素?答案是它們指的是同一個元素,awk 將打印 "Mr. Whipple",如同第一個代碼片斷一樣。雖然看上去可能有點怪,但 awk 在幕後卻一直使用數組的字符串下標! 


瞭解了這個奇怪的真相之後,我們中的一些人可能想要執行類似於以下的古怪代碼: 


myarr["name"]="Mr. Whipple"


print myarr["name"]


這段代碼不僅不會產生錯誤,而且它的功能與前面的示例完全相同,也將打印 "Mr. Whipple"!可以看到,awk 並沒有限制我們使用純整數下標;如果我們願意,可以使用字符串下標,而且不會產生任何問題。只要我們使用非整數數組下標,如 myarr["name"],那麼我們就在使用關聯數組。從技術上講,如果我們使用字符串下標,awk 的後臺操作並沒有什麼不同(因爲即便使用“整數”下標,awk 還是會將它看作是字符串)。但是,應該將它們稱作關聯數組 -- 它聽起來很酷,而且會給您的上司留下印象。字符串化下標是我們的小祕密。;) 


數組工具
談到數組時,awk 給予我們許多靈活性。可以使用字符串下標,而且不需要連續的數字序列下標(例如,可以定義 myarr[1] 和 myarr[1000],但不定義其它所有元素)。雖然這些都很有用,但在某些情況下,會產生混淆。幸好,awk 提供了一些實用功能有助於使數組變得更易於管理。


首先,可以刪除數組元素。如果想要刪除數組 fooarray 的元素 1,輸入:


delete fooarray[1]


而且,如果想要查看是否存在某個特定數組元素,可以使用特殊的 "in" 布爾運算符,如下所示: 


if ( 1 in fooarray ) {


                     print "Ayep!  It's there."


} else {


                     print "Nope!  Can't find it."


}


格式化輸出
雖然大多數情況下 awk 的 print 語句可以完成任務,但有時我們還需要更多。在那些情況下,awk 提供了兩個我們熟知的老朋友 printf() 和 sprintf()。是的,如同其它許多 awk 部件一樣,這些函數等同於相應的 C 語言函數。printf() 會將格式化字符串打印到 stdout,而 sprintf() 則返回可以賦值給變量的格式化字符串。如果不熟悉 printf() 和 sprintf(),介紹 C 語言的文章可以讓您迅速瞭解這兩個基本打印函數。在 Linux 系統上,可以輸入 "man 3 printf" 來查看 printf() 幫助頁面。


以下是一些 awk sprintf() 和 printf() 的樣本代碼。可以看到,它們幾乎與 C 語言完全相同。


x=1


b="foo"


printf("%s got a %d on the last test\n","Jim",83)


myout=("%s-%d",b,x)


print myout


      此代碼將打印:


              Jim got a 83 on the last test


foo-1


字符串函數
awk 有許多字符串函數,這是件好事。在 awk 中,確實需要字符串函數,因爲不能象在其它語言(如 C、C++ 和 Python)中那樣將字符串看作是字符數組。例如,如果執行以下代碼:


mystring="How are you doing today?"


print mystring[3]


將會接收到一個錯誤,如下所示:


awk: string.gawk:59: fatal: attempt to use scalar as array


噢,好吧。雖然不象 Python 的序列類型那樣方便,但 awk 的字符串函數還是可以完成任務。讓我們來看一下。


首先,有一個基本 length() 函數,它返回字符串的長度。以下是它的使用方法:


print length(mystring)


此代碼將打印值:


24


好,繼續。下一個字符串函數叫作 index,它將返回子字符串在另一個字符串中出現的位置,如果沒有找到該字符串則返回 0。使用 mystring,可以按以下方法調用它:


print index(mystring,"you")


awk 會打印:


9


讓我們繼續討論另外兩個簡單的函數,tolower() 和 toupper()。與您猜想的一樣,這兩個函數將返回字符串並且將所有字符分別轉換成小寫或大寫。請注意,tolower() 和 toupper() 返回新的字符串,不會修改原來的字符串。這段代碼:


print tolower(mystring)


print toupper(mystring)


print mystring


……將產生以下輸出:


how are you doing today?


HOW ARE YOU DOING TODAY?


How are you doing today?


到現在爲止一切不錯,但我們究竟如何從字符串中選擇子串,甚至單個字符?那就是使用 substr() 的原因。以下是 substr() 的調用方法:


mysub=substr(mystring,startpos,maxlen)


mystring 應該是要從中抽取子串的字符串變量或文字字符串。startpos 應該設置成起始字符位置,maxlen 應該包含要抽取的字符串的最大長度。請注意,我說的是最大長度;如果 length(mystring) 比 startpos+maxlen 短,那麼得到的結果就會被截斷。substr() 不會修改原始字符串,而是返回子串。以下是一個示例:


print substr(mystring,9,3)


awk 將打印:


you


如果您通常用於編程的語言使用數組下標訪問部分字符串(以及不使用這種語言的人),請記住 substr() 是 awk 代替方法。需要使用它來抽取單個字符和子串;因爲 awk 是基於字符串的語言,所以會經常用到它。


一些更耐人尋味的函數
首先是 match()。match() 與 index() 非常相似,它與 index() 的區別在於它並不搜索子串,它搜索的是規則表達式。match() 函數將返回匹配的起始位置,如果沒有找到匹配,則返回 0。此外,match() 還將設置兩個變量,叫作 RSTART 和 RLENGTH。RSTART 包含返回值(第一個匹配的位置),RLENGTH 指定它佔據的字符跨度(如果沒有找到匹配,則返回 -1)。通過使用 RSTART、RLENGTH、substr() 和一個小循環,可以輕鬆地迭代字符串中的每個匹配。以下是一個 match() 調用示例:


print match(mystring,/you/), RSTART, RLENGTH


awk 將打印:


9 9 3


字符串替換
現在,我們將研究兩個字符串替換函數,sub() 和 gsub()。這些函數與目前已經討論過的函數略有不同,因爲它們確實修改原始字符串。以下是一個模板,顯示瞭如何調用 sub():


sub(regexp,replstring,mystring)


調用 sub() 時,它將在 mystring 中匹配 regexp 的第一個字符序列,並且用 replstring 替換該序列。sub() 和 gsub() 用相同的自變量;唯一的區別是 sub() 將替換第一個 regexp 匹配(如果有的話),gsub() 將執行全局替換,換出字符串中的所有匹配。以下是一個 sub() 和 gsub() 調用示例:


sub(/o/,"O",mystring)


print mystring


mystring="How are you doing today?"


gsub(/o/,"O",mystring)


print mystring


必須將 mystring 復位成其初始值,因爲第一個 sub() 調用直接修改了 mystring。在執行時,此代碼將使 awk 輸出:


HOw are you doing today?


HOw are yOu dOing tOday?


當然,也可以是更復雜的規則表達式。我把測試一些複雜規則表達式的任務留給您來完成。


通過介紹函數 split(),我們來彙總一下已討論過的函數。split() 的任務是“切開”字符串,並將各部分放到使用整數下標的數組中。以下是一個 split() 調用示例:


numelements=split("Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec",mymonths,",")


調用 split() 時,第一個自變量包含要切開文字字符串或字符串變量。在第二個自變量中,應該指定 split() 將填入片段部分的數組名稱。在第三個元素中,指定用於切開字符串的分隔符。split() 返回時,它將返回分割的字符串元素的數量。split() 將每一個片段賦值給下標從 1 開始的數組,因此以下代碼:


print mymonths[1],mymonths[numelements]


……將打印:


Jan Dec


特殊字符串形式
簡短註釋 -- 調用 length()、sub() 或 gsub() 時,可以去掉最後一個自變量,這樣 awk 將對 $0(整個當前行)應用函數調用。要打印文件中每一行的長度,使用以下 awk 腳本:


{


                 print length() 


}


sed使用規則
       sed 是很有用(但常被遺忘)的 UNIX 流編輯器。sed是十分強大和小巧的文本流編輯器。使用sed 可以執行字符串替換、創建更大的 sed 腳本以及使用 sed 的附加、插入和更改行命令。在以批處理方式編輯文件或以有效方式創建 shell 腳本來修改現有文件方面,它是十分理想的工具。


sed 示例
sed 通過對輸入數據執行任意數量用戶指定的編輯操作(“命令”)來工作。sed 是基於行的,因此按順序對每一行執行命令。然後,sed 將其結果寫入標準輸出 (stdout),它不修改任何輸入文件。


讓我們看一些示例。頭幾個會有些奇怪,因爲我要用它們演示 sed 如何工作,而不是執行任何有用的任務。然而,如果您是 sed 新手,那麼理解它們是十分重要的。下面是第一個示例:


$ sed -e 'd' /etc/services


如果輸入該命令,將得不到任何輸出。那麼,發生了什麼?在該例中,用一個編輯命令 'd' 調用 sed。sed 打開 /etc/services 文件,將一行讀入其模式緩衝區,執行編輯命令(“刪除行”),然後打印模式緩衝區(緩衝區已爲空)。然後,它對後面的每一行重複這些步驟。這不會產生輸出,因爲 "d" 命令除去了模式緩衝區中的每一行!


在該例中,還有幾件事要注意。首先,根本沒有修改 /etc/services。這還是因爲 sed 只讀取在命令行指定的文件,將其用作輸入 -- 它不試圖修改該文件。第二件要注意的事是 sed 是面向行的。'd' 命令不是簡單地告訴 sed 一下子刪除所有輸入數據。相反,sed 逐行將 /etc/services 的每一行讀入其稱爲模式緩衝區的內部緩衝區。一旦將一行讀入模式緩衝區,它就執行 'd' 命令,然後打印模式緩衝區的內容(在本例中沒有內容)。我將在後面爲您演示如何使用地址範圍來控制將命令應用到哪些行 -- 但是,如果不使用地址,命令將應用到所有行。第三件要注意的事是括起 'd' 命令的單引號的用法。養成使用單引號來括起 sed 命令的習慣是個好注意,這樣可以禁用 shell 擴展。


另一個 sed 示例
下面是使用 sed 從輸出流除去 /etc/services 文件第一行的示例:


$ sed -e '1d' /etc/services | more


如您所見,除了前面有 '1' 之外,該命令與第一個 'd' 命令十分類似。如果您猜到 '1' 指的是第一行,那您就猜對了。與第一個示例中只使用 'd' 不同的是,這一次使用的 'd' 前面有一個可選的數字地址。通過使用地址,可以告訴 sed 只對某一或某些特定行進行編輯。


地址範圍
現在,讓我們看一下如何指定地址範圍。在本例中,sed 將刪除輸出的第 1 到 10 行:


$ sed -e '1,10d' /etc/services | more


當用逗號將兩個地址分開時,sed 將把後面的命令應用到從第一個地址開始、到第二個地址結束的範圍。在本例中,將 'd' 命令應用到第 1 到 10 行(包括這兩行)。所有其它行都被忽略。


帶規則表達式的地址
現在演示一個更有用的示例。假設要查看 /etc/services 文件的內容,但是對查看其中包括的註釋部分不感興趣。如您所知,可以通過以 '#' 字符開頭的行在 /etc/services 文件中放置註釋。爲了避免註釋,我們希望 sed 刪除以 '#' 開始的行。以下是具體做法:


$ sed -e '/^#/d' /etc/services | more


試一下該例,看看發生了什麼。您將注意到,sed 成功完成了預期任務。現在,讓我們分析發生的情況:


要理解 '/^#/d' 命令,首先需要對其剖析。首先,讓我們除去 'd' -- 這是我們前面所使用的同一個刪除行命令。新增加的是 '/^#/' 部分,它是一種新的規則表達式地址。規則表達式地址總是由斜槓括起。它們指定一種模式,緊跟在規則表達式地址之後的命令將僅適用於正好與該特定模式匹配的行。因此,'/^#/' 是一個規則表達式。(規則表達式的有關規定可以參見本文前面的內容)


例如:


$ sed -e '/regexp/d' /path/to/my/test/file | more


這將導致 sed 刪除任何匹配的行。


對比如下的命令:


$ sed -n -e '/regexp/p' /path/to/my/test/file | more


請注意新的 '-n' 選項,該選項告訴 sed 除非明確要求打印模式空間,否則不這樣做。您還會注意到,我們用 'p' 命令替換了 'd' 命令,如您所猜想的那樣,這明確要求 sed 打印模式空間。就這樣,將只打印匹配部分。


有關地址的更多內容
目前爲止,我們已經看到了行地址、行範圍地址和 regexp 地址。但是,還有更多的可能。我們可以指定兩個用逗號分開的規則表達式,sed 將與所有從匹配第一個規則表達式的第一行開始,到匹配第二個規則表達式的行結束(包括該行)的所有行匹配。


例如,以下命令將打印從包含 "BEGIN" 的行開始,並且以包含 "END" 的行結束的文本塊:


$ sed -n -e '/BEGIN/,/END/p' /my/test/file | more


如果沒發現 "BEGIN",那麼將不打印數據。如果發現了 "BEGIN",但是在這之後的所有行中都沒發現 "END",那麼將打印所有後續行。發生這種情況是因爲 sed 面向流的特性 -- 它不知道是否會出現 "END"。


C 源代碼示例


如果只要打印 C 源文件中的 main() 函數,可輸入:


$ sed -n -e '/main[[:space:]]*(/,/^)/p' sourcefile.c | more


該命令有兩個規則表達式 '/main[[:space:]]*(/' 和 '/^}/',以及一個命令 'p'。第一個規則表達式將與後面依次跟有任意數量的空格或製表鍵以及開始圓括號的字符串 "main" 匹配。這應該與一般 ANSI C main() 聲明的開始匹配。


在這個特別的規則表達式中,出現了 '[[:space:]]' 字符類。這只是一個特殊的關鍵字,它告訴 sed 與 TAB 或空格匹配。如果願意的話,可以不輸入 '[[:space:]]',而輸入 '[',然後是空格字母,然後是 -V,然後再輸入製表鍵字母和 ']' -- Control-V 告訴 bash 要插入“真正”的製表鍵,而不是執行命令擴展。使用 '[[:space:]]' 命令類(特別是在腳本中)會更清楚。


現在看一下第二個 regexp。'/^}' 將與任何出現在新行行首的 '}' 字符匹配。如果代碼的格式很好,那麼這將與 main() 函數的結束花括號匹配。如果格式不好,則不會正確匹配 -- 這是執行模式匹配任務的一件棘手之事。因爲是處於 '-n' 安靜方式,所以 'p' 命令還是完成其慣有任務,即明確告訴 sed 打印該行。試着對 C 源文件運行該命令 -- 它應該輸出整個 main() { } 塊,包括開始的 "main()" 和結束的 '}'。


替換
讓我們看一下 sed 最有用的命令之一,替換命令。使用該命令,可以將特定字符串或匹配的規則表達式用另一個字符串替換。


下面是該命令最基本用法的示例:


$ sed -e 's/foo/bar/' myfile.txt 


上面的命令將 myfile.txt 中每行第一次出現的 'foo'(如果有的話)用字符串 'bar' 替換,然後將該文件內容輸出到標準輸出。請注意,我說的是每行第一次出現,儘管這通常不是您想要的。在進行字符串替換時,通常想執行全局替換。也就是說,要替換每行中的所有出現,如下所示: 


$ sed -e 's/foo/bar/g' myfile.txt 


在最後一個斜槓之後附加的 'g' 選項告訴 sed 執行全局替換。


關於 's///' 替換命令,還有其它幾件要了解的事。首先,它是一個命令,並且只是一個命令,在所有上例中都沒有指定地址。這意味着,'s///' 還可以與地址一起使用來控制要將命令應用到哪些行,如下所示:


$ sed -e '1,10s/enchantment/entrapment/g' myfile2.txt 


上例將導致用短語 'entrapment' 替換所有出現的短語 'enchantment',但是隻在第一到第十行(包括這兩行)上這樣做。


$ sed -e '/^$/,/^END/s/hills/mountains/g' myfile3.txt 


該例將用 'mountains' 替換 'hills',但是,只從空行開始,到以三個字符 'END' 開始的行結束(包括這兩行)的文本塊上這樣做。


關於 's///' 命令的另一個妙處是 '/' 分隔符有許多替換選項。如果正在執行字符串替換,並且規則表達式或替換字符串中有許多斜槓,則可以通過在 's' 之後指定一個不同的字符來更改分隔符。例如,下例將把所有出現的 /usr/local 替換成 /usr:


$ sed -e 's:/usr/local:/usr:g' mylist.txt 


在該例中,使用冒號作爲分隔符。如果需要在規則表達式中指定分隔符字符,可以在它前面加入反斜槓。


規則表達式混亂
目前爲止,我們只執行了簡單的字符串替換。雖然這很方便,但是我們還可以匹配規則表達式。例如,以下 sed 命令將匹配從 '<' 開始、到 '>' 結束、並且在其中包含任意數量字符的短語。下例將刪除該短語(用空字符串替換):


$ sed -e 's/<.*>//g' myfile.html 


這是要從文件除去 HTML 標記的第一個很好的 sed 腳本嘗試,但是由於規則表達式的特有規則,它不會很好地工作。原因何在?當 sed 試圖在行中匹配規則表達式時,它要在行中查找最長的匹配。在我的前一篇 sed 文章中,這不成問題,因爲我們使用的是 'd' 和 'p' 命令,這些命令總要刪除或打印整行。但是,在使用 's///' 命令時,確實有很大不同,因爲規則表達式匹配的整個部分將被目標字符串替換,或者,在本例中,被刪除。這意味着,上例將把下行:


<b>This</b> is what <b>I</b> meant. 


變成:meant. 


我們要的不是這個,而是:This is what I meant. 


幸運的是,有一種簡便方法來糾正該問題。我們不輸入“'<' 字符後面跟有一些字符並以 '>' 字符結束”的規則表達式,而只需輸入一個“'<' 字符後面跟有任意數量非 '>' 字符並以 '>' 字符結束”的規則表達式。這將與最短、而不是最長的可能性匹配。新命令如下:


             $ sed -e 's/<[^>]*>//g' myfile.html 


在上例中,'[^>]' 指定“非 '>'”字符,其後的 '*' 完成該表達式以表示“零或多個非 '>' 字符”。對幾個 html 文件測試該命令,將它們管道輸出到 "more",然後仔細查看其結果。


更多字符匹配
'[ ]' 規則表達式語法還有一些附加選項。要指定字符範圍,只要字符不在第一個或最後一個位置,就可以使用 '-',如下所示:


             '[a-x]*' 


這將匹配零或多個全部爲 'a'、'b'、'c'...'v'、'w'、'x' 的字符。另外,可以使用 '[:space:]' 字符類來匹配空格(字符類的相關信息可以參見本文前面部分內容)。


高級替換功能
我們已經看到如何執行簡單甚至有些複雜的直接替換,但是 sed 還可以做更多的事。實際上可以引用匹配規則表達式的部分或全部,並使用這些部分來構造替換字符串。作爲示例,假設您正在回覆一條消息。下例將在每一行前面加上短語 "ralph said: ":


$ sed -e 's/.*/ralph said: &/' origmsg.txt 


輸出如下:


ralph said: Hiya Jim, ralph said: ralph said: 


I sure like this sed stuff! ralph said: 


該例的替換字符串中使用了 '&' 字符,該字符告訴 sed 插入整個匹配的規則表達式。因此,可以將與 '.*' 匹配的任何內容(行中的零或多個字符的最大組或整行)插入到替換字符串中的任何位置,甚至多次插入。這非常好,但 sed 甚至更強大。


那些極好的帶反斜槓的圓括號


's///' 命令甚至比 '&' 更好,它允許我們在規則表達式中定義區域,然後可以在替換字符串中引用這些特定區域。作爲示例,假設有一個包含以下文本的文件: 


bar oni eeny meeny miny larry curly moe jimmy the weasel 


現在假設要編寫一個 sed 腳本,該腳本將把 "eeny meeny miny" 替換成 "Victor eeny-meeny Von miny" 等等。要這樣做,首先要編寫一個由空格分隔並與三個字符串匹配的規則表達式:


'.* .* .*' 


現在,將在其中每個感興趣的區域兩邊插入帶反斜槓的圓括號來定義區域:


'\(.*\) \(.*\) \(.*\)' 


除了要定義三個可在替換字符串中引用的邏輯區域以外,該規則表達式的工作原理將與第一個規則表達式相同。下面是最終腳本:


$ sed -e 's/\(.*\) \(.*\) \(.*\)/Victor \1-\2 Von \3/' myfile.txt 


如您所見,通過輸入 '\x'(其中,x 是從 1 開始的區域號)來引用每個由圓括號定界的區域。輸入如下:


Victor foo-bar Von oni Victor eeny-meeny Von miny Victor larry-curly Von moe Victor jimmy-the Von weasel 


隨着對 sed 越來越熟悉,您可以花最小力氣來進行相當強大的文本處理。您可能想如何使用熟悉的腳本語言來處理這種問題 -- 能用一行代碼輕易實現這樣的解決方案嗎?


組合使用
在開始創建更復雜的 sed 腳本時,需要有輸入多個命令的能力。有幾種方法這樣做。首先,可以在命令之間使用分號。例如,以下命令系列使用 '=' 命令和 'p' 命令,'=' 命令告訴 sed 打印行號,'p' 命令明確告訴 sed 打印該行(因爲處於 '-n' 模式)。


$ sed -n -e '=;p' myfile.txt 


無論什麼時候指定了兩個或更多命令,都按順序將每個命令應用到文件的每一行。在上例中,首先將 '=' 命令應用到第 1 行,然後應用 'p' 命令。接着,sed 繼續處理第 2 行,並重復該過程。雖然分號很方便,但是在某些場合下,它不能正常工作。另一種替換方法是使用兩個 -e 選項來指定兩個不同的命令: 


$ sed -n -e '=' -e 'p' myfile.txt 


然而,在使用更爲複雜的附加和插入命令時,甚至多個 '-e' 選項也不能幫我們的忙。對於複雜的多行腳本,最好的方法是將命令放入一個單獨的文件中。然後,用 -f 選項引用該腳本文件:


$ sed -n -f mycommands.sed myfile.txt 


這種方法雖然可能不太方便,但總是管用。


一個地址的多個命令
有時,可能要指定應用到一個地址的多個命令。這在執行許多 's///' 以變換源文件中的字和語法時特別方便。要對一個地址執行多個命令,可在文件中輸入 sed 命令,然後使用 '{ }' 字符將這些命令分組,如下所示:


1,20{    s/[Ll]inux/GNU\/Linux/g     s/samba/Samba/g        s/posix/POSIX/g } 


上例將把三個替換命令應用到第 1 行到第 20 行(包括這兩行)。還可以使用規則表達式地址或者二者的組合:


1,/^END/{         s/[Ll]inux/GNU\/Linux/g         s/samba/Samba/g         s/posix/POSIX/g        p } 


該例將把 '{ }' 之間的所有命令應用到從第 1 行開始,到以字母 "END" 開始的行結束(如果在源文件中沒發現 "END",則到文件結束)的所有行。


附加、插入和更改行
既然在單獨的文件中編寫 sed 腳本,我們可以利用附加、插入和更改行命令。這些命令將在當前行之後插入一行,在當前行之前插入一行,或者替換模式空間中的當前行。它們也可以用來將多行插入到輸出。插入行命令用法如下:


i\ This line will be inserted before each line 


如果不爲該命令指定地址,那麼它將應用到每一行,併產生如下的輸出:


             This line will be inserted before each line line 1 here 


             This line will be inserted before each line line 2 here 


             This line will be inserted before each line line 3 here 


             This line will be inserted before each line line 4 here 


如果要在當前行之前插入多行,可以通過在前一行之後附加一個反斜槓來添加附加行,如下所示:


             i\ insert this line\ and this one\ and this one\ and, uh, this one too. 


附加命令的用法與之類似,但是它將把一行或多行插入到模式空間中的當前行之後。其用法如下:


             a\ insert this line after each line.  Thanks! :) 


另一方面,“更改行”命令將實際替換模式空間中的當前行,其用法如下:


             c\ You're history, original line! Muhahaha! 


因爲附加、插入和更改行命令需要在多行輸入,所以將把它們輸入到一個文本 sed 腳本中,然後通過使用 '-f' 選項告訴 sed 執行它們。使用其它方法將命令傳遞給 sed 會出現問題。


使用 sed 的幾個示例
這些示例不僅演示 sed 的能力,而且還做一些真正巧妙(和方便)的事。例如,在本文的後半部,將爲您演示如何設計一個 sed 腳本來將 .QIF 文件從 Intuit 的 Quicken 金融程序轉換成具有良好格式的文本文件。在那樣做之前,我們將看一下不怎麼複雜但卻很有用的 sed 腳本。


l        文本轉換


第一個實際腳本將 UNIX 風格的文本轉換成 DOS/Windows 格式。您可能知道,基於 DOS/Windows 的文本文件在每一行末尾有一個 CR(回車)和 LF(換行),而 UNIX 文本只有一個換行。有時可能需要將某些 UNIX 文本移至 Windows 系統,該腳本將爲您執行必需的格式轉換。


             $ sed -e 's/$/\r/' myunix.txt > mydos.txt 


      在該腳本中,'$' 規則表達式將與行的末尾匹配,而 '\r' 告訴 sed 在其之前插入一個回車。在換行之前插入回車,立即,每一行就以 CR/LF 結束。請注意,僅當使用 GNU sed 3.02.80 或以後的版本時,纔會用 CR 替換 '\r'。如果還沒有安裝 GNU sed 3.02.80,請在我的第一篇 sed 文章中查看如何這樣做的說明。


我已記不清有多少次在下載一些示例腳本或 C 代碼之後,卻發現它是 DOS/Windows 格式。雖然很多程序不在乎 DOS/Windows 格式的 CR/LF 文本文件,但是有幾個程序卻在乎 -- 最著名的是 bash,只要一遇到回車,它就會出問題。以下 sed 調用將把 DOS/Windows 格式的文本轉換成可信賴的 UNIX 格式:


             $ sed -e 's/.$//' mydos.txt > myunix.txt 


      該腳本的工作原理很簡單:替代規則表達式與一行的最末字符匹配,而該字符恰好就是回車。我們用空字符替換它,從而將其從輸出中徹底刪除。如果使用該腳本並注意到已經刪除了輸出中每行的最末字符,那麼,您就指定了已經是 UNIX 格式的文本文件。也就沒必要那樣做了!


l        反轉行


下面是另一個方便的小腳本。與大多數 Linux 發行版中包括的 "tac" 命令一樣,該腳本將反轉文件中行的次序。"tac" 這個名稱可能會給人以誤導,因爲 "tac" 不反轉行中字符的位置(左和右),而是反轉文件中行的位置(上和下)。用 "tac" 處理以下文件:


             foo bar oni 


      ....將產生以下輸出:


oni bar foo 


      可以用以下 sed 腳本達到相同目的: 


             $ sed -e '1!G;h;$!d' forward.txt > backward.txt 


      如果登錄到恰巧沒有 "tac" 命令的 FreeBSD 系統,將發現該 sed 腳本很有用。雖然方便,但最好還是知道該腳本爲什麼那樣做。讓我們對它進行討論。


反轉解釋:首先,該腳本包含三個由分號隔開的單獨 sed 命令:'1!G'、'h' 和 '$!d'。現在,需要好好理解用於第一個和第三個命令的地址。如果第一個命令是 '1G',則 'G' 命令將只應用第一行。然而,還有一個 '!' 字符 -- 該 '!' 字符忽略該地址,即,'G' 命令將應用到除第一行之外的所有行。'$!d' 命令與之類似。如果命令是 '$d',則將只把 'd' 命令應用到文件中的最後一行('$' 地址是指定最後一行的簡單方式)。然而,有了 '!' 之後,'$!d' 將把 'd' 命令應用到除最後一行之外的所有行。現在,我們所要理解的是這些命令本身做什麼。


當對上面的文本文件執行反轉腳本時,首先執行的命令是 'h'。該命令告訴 sed 將模式空間(保存正在處理的當前行的緩衝區)的內容複製到保留空間(臨時緩衝區)。然後,執行 'd' 命令,該命令從模式空間中刪除 "foo",以便在對這一行執行完所有命令之後不打印它。


現在,第二行。在將 "bar" 讀入模式空間之後,執行 'G' 命令,該命令將保留空間的內容 ("foo\n") 附加到模式空間 ("bar\n"),使模式空間的內容爲 "bar\n\foo\n"。'h' 命令將該內容放回保留空間保護起來,然後,'d' 從模式空間刪除該行,以便不打印它。


對於最後的 "oni" 行,除了不刪除模式空間的內容(由於 'd' 之前的 '$!')以及將模式空間的內容(三行)打印到標準輸出之外,重複同樣的步驟。


linux常用腳本和函數
l        #查找當前目錄中是否存在指定目錄,若不存在,則創建之


function mkdir_1


{


                     if test ! -d $1


                     then


                            mkdir $1


                     fi


}


l        #將指定文件中的"prefix = .*"串替換爲"prefix=\/home\/gnome-unicore-install2\/usr/" 


#可以用來作爲sed用法的參考


function modify_prefix 


{


             chmod +w $1 


             cp $1 $1.bak 


             sed 's/prefix = .*/prefix=\/home\/gnome-unicore-install2\/usr/g' $1.bak > $1 


             rm $1.bak 


}


l        #將指定文件中的"^LDFLAGS =.*"串替換爲"LDFLAGS = -rdynamic -lgdk_pixbuf -lgtk -lgdk -lgmodule -lglib -ldl -lXext -lX11 -lm"


#change_gnome-config FILENAME


function change_gnome-config


{


                     cp $1 $1.bak


                    sed 's/^LDFLAGS =.*/LDFLAGS = -rdynamic -lgdk_pixbuf -lgtk -lgdk -lgmodule -lglib -ldl -lXext -lX11 -lm /g' $1.bak> $1     


                    rm $1.bak


}


l        #刪除指定文件的含有指定字符的行


#格式:delete_line filename "word_contain"


function delete_line


{


                     chmod +w $1


                     cp $1 $1.bak


                     cat $1.bak | grep -v -e "$2" >$1       


}


l        #用途:刪除文件中包含line1或(和?)line2的行


#格式:delete_line filename line1 line2


function delete_line_no


{


             chmod +w $1


             cp $1 $1.bak


             sed  $2,$3'd' $1.bak>$1


             rm $1.bak


}


l        #用途:在LINE_NO指定的行插入字符串CONTENT


#可以用來作爲sed用法的參考


#格式: add_line FILENAME LINE_NO CONTENT


function add_line


{


                     chmod +w $1


             cp $1 $1.bak


             sed -e $2 'i\' "$3" '' $1.bak > $1


             rm $1.bak


}


l        #用途:檢查含有"PC24"代碼的程序並打印出來


#格式: check_PC24 //after installation  


function check_PC24


{


                   echo "now comes the PC24 checking..."


                   . $COMMAND_UNICORE/shell/shell_PC24 >& /dev/null


                   if test -s $COMMAND_UNICORE/PC24_result


                 then :


               echo "The following file contains PC24 problems: $COMMAND_UNICORE/PC24_result "


                 else


                      echo "No PC24 problem found"


                   fi


}


l        #打印標題


displayheader() {


                   echo "   *****************************************"


                   echo "   *         IeeeCC754 testing tool           *"


                   echo "   *****************************************"


                   echo " "


}


l        #打印一個菜單的做法


displayplatformmenu() {


            #clear the screen


            clear


            displayheader


            echo "   a) SunSparc "


            echo "   b) IntelPentium "


            echo "   c) AMD "


            echo "   d) Unicore32 "


            echo "   e) Unicore32(with FP2001) "


            echo " "


            echo  -n "   select a Platform > "


}


l        #接收一個菜單輸入


displayplatformmenu


read answer


case ${answer} in


            a) TARGET="BasicOp";;


            b) TARGET="Conversion";; 


            *) badchoice;;


esac


l        #查找當前目錄下是否存在file_name文件


#可以用來作爲if用法的參考


detectfile_name() {


                   if [ ! -f file_name ]


                   then


                        echo "Error: file_name does not exist.  Please check"


                 exit 1;


                   else


                        echo "OK,the directy is exist"


                   fi


}


l        #將參數指定的一個或多個目錄項以及其下的多級子目錄下的所有文件名和目錄名轉換爲小寫。


cvitem() 


{


echo "mv $1 `dirname $1`/`basename $1 | tr \


'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`"


}


[ $# = 0 ] && { echo "Usage: lcdir item1 item2 ..."; exit; }


for item in $*     #可以用來作爲for用法的參考


do


[ "`dirname $item`" != "`basename $item`" ] && {


[ -d $item ] &&


{


for subitem in `ls $item`


do


cvlc $item/$subitem


done


}


cvitem $item


}


done


l        #一個login的例子


if ($?path) then 


set path=($HOME/bin $path) 


else 


set path=($HOME/bin /usr/bin .) 


endif 


if ( ! $ {?DT} ); then 


stty dec new 


tset -I -Q 


endif 


set mail=/usr/spool/mail/$USER 


l        #關於if使用的幾個例子


n        #執行一個命令或程序之前,先檢查該命令是否存在,然後才執行


if [ -x /sbin/quotaon ] ; then 


echo "Turning on Quota for root filesystem" 


/sbin/quotaon / 


fi 


n        #得到Hostname 


#!/bin/sh 


if [ -f /etc/HOSTNAME ] ; then 


HOSTNAME=`cat /etc/HOSTNAME` 


else 


HOSTNAME=localhost 


fi 


n        #如果某個設定檔允許有好幾個位置的話,例如crontab,可利用if then elif fi來找尋


if [ -f /etc/crontab ] ; then  #[ -f /etc/crontab ]等價於test -f /etc/crontab


CRONTAB="/etc/crontab" 


elif [ -f /var/spool/cron/crontabs/root ] ; then 


CRONTAB="/var/spool/cron/crontabs/root" 


elif [ -f /var/cron/tabs/root ] ; then 


CRONTAB="/var/cron/tabs/root" 


fi 


export CRONTAB 


n        #利用uname來判斷目前系統,並分別做各系統狀況不同的事。


SYSTEM=`uname -s` 


if [ $SYSTEM = "Linux" ] ; then 


echo "Linux" 


elif [ $SYSTEM = "FreeBSD" ] ; then 


echo "FreeBSD" 


elif [ $SYSTEM = "Solaris" ] ; then 


echo "Solaris" 


else 


echo "What?" 


fi 


l        #關於while使用的幾個例子


n        #無條件循環


while : ; do 


echo "do something forever here" 


sleep 5 


done


linux常用命令
以下只說明各指令的基本用法, 若需詳細說明, 請用 man 去讀詳細的 manual.


關於文件/目錄處理的指令:
1. ls


這是最基本的文件指令。 ls 的意義爲 "list",也就是將某一個目錄或是某一個文件的內容顯示出來。


如果你在下 ls 指令後面沒有跟任何的文件名,它將會顯示出目前目錄中所有文件。


也可以在 ls 後面加上所要察看的目錄名稱或文件的名稱,如:


% ls /home2/X11R5


% ls first 


ls 有一些特別的參數,可以給予使用者更多有關的信息,如下:


-a : 在 UNIX 中若一個目錄或文件名字的第一個字符爲 "." , 則使用 ls


將不會顯示出這個文件的名字,我們稱此類文件爲隱藏文件。如果我們要察看這類文件,則必須加上參數 -a 。


-l : 這個參數代表使用 ls 的長( long )格式,可以顯示更多的信息,如文件存取權,文件擁有者( owner ),文件大小,文件最後更新日期,甚而 symbolic link 的文件是 link 到那一個文件等等。例如:


% ls -l 


drwx--x--x 2 jjtseng 512 Aug 8 05:08 18


drwx--x--x 2 jjtseng 512 Aug 8 22:00 19


-rw------- 1 jjtseng 566 Aug 8 05:28 makefile


2. cp      


cp 這個指令的意義是複製("COPY") , 也就是將一個或多個文件複製成另一個文件或者是將其複製到另一個目錄去。


cp 的用法如下:


cp f1 f2 : 將文件名爲 f1 的文件複製一份爲文件名爲 f2 的文件。


cp f1 f2 f3 ... dir : 將文件 f1 f2 f3 ... 都以相同的文件名複製一份放到目錄 dir 裏面。


cp -r dir1 dir2 : 將 dir1 的全部內容全部複製到 dir2 裏面。


cp 也有一些參數,如下:


-i : 此參數是當已經有文件名爲 f2 的文件時,若逕自使用 cp 將會將原來 f2的內容覆蓋,因此在要覆蓋之前先詢問使用者。如使用者的回答是y(yes)才執行復制的動作。


-r : 此參數是用來做遞迴複製用,可遞歸的將整個目錄都複製到另一個目錄中。 


3. mv


mv 的意義爲 move , 主要是將一文件改名或移動到另一個目錄。與cp類似,它也有三種格式:


mv f1 f2 : 將文件名爲 f1 的文件變更成文件名爲 f2 的文件。


mv dir1 dir2 : 將文件名爲 dir1 的目錄變更成文件名爲 dir2 的目錄。


mv f1 f2 f3 ... dir : 將文件 f1 f2 f3 ... 都移至目錄 dir 裏面。


mv 的參數有兩個,-f 和 -i , 其中 -i 的意義與 cp 中的相同,均是 interactive的意思。而 -f 爲強迫( force ) , 就是不管有沒有同名的文件,反正就是要執行,所有其他的參數遇到 -f 均會失效。


4. rm


rm 的意義是 remove ,也就是用來刪除一個文件的指令。需要注意的是,在 UNIX 中一個被刪除的文件除非是系統恰好做了備份,否則是無法像 DOS 裏面一樣還能夠恢復的。所以在做 rm 動作的時候使用者應該要特別小心。


rm 的格式如下:


rm f1 f2 f3 .....


rm 的參數比較常用的有幾個: -f , -i , 與 -r


-f : 將會使得系統在刪除時,不提出任何警告訊息。


-i : 在刪除文件之前均會詢問是否真要刪除。


-r : 遞歸的刪除,可用於刪除目錄。


5. mkdir


mkdir 是一個讓使用者建立一個目錄的指令。你可以在一個目錄底下使用 mkdir 建立一個子目錄,使用的方法如下:


mkdir dirname1 [ dirname2 ... ]


8. pwd


pwd 會將當前目錄的路徑( path )顯示出來


9. cat/more/less


以上三個指令均爲察看文件內容的指令。cat 的意義是concatenate,實際就是把文件的內容顯示出來的意思。


cat 有許多奇怪的參數,較常爲人所使用的是 -n 參數,也就是把顯示出來的內容加上行號。 


cat 的用法如下:


cat [-n] :自標準輸入讀取內容,可以用 pipe 將別的指令的輸出轉向給 cat 。


cat [-n] filename : 將 filename 的內容讀進來,顯示在標準輸出上。


問題在於 cat 它是不會停下來的,因此並不好用( 試想如果一個螢幕二十四行,而一個文件四百行,cat 一出來將會噼裏啪啦不斷的捲上去,使用者很難據此得到他們所需的信息。) 所以常常麼使用 more 和less來幫助。


more 可以讓文件根據控制檯的模式一頁頁的顯示出來,再根據使用者的要求換頁或者換行。如果使用者要在某一個文件中搜尋一個特定的字符串,則按 / 然後跟着打所要搜尋的單字即可進行搜尋。


more 的使用法如下:


more filename


如果你在使用中覺得已經看到了所要看的部份,可以按'q'離開 more 的使用。


less 的用法與 more 極類似,原先它就是爲了彌補 more 只能往前方卷頁的缺點而設計。 


Less 的用法如下:


less filename 


它與 more 不同的是它可以按 y 來往上卷一行,並且可以用"?"來往回搜尋你所要找的字符。


10. chmod


chmod用來改變文件存取模式( change mode ) 。在linux中,一個文件有可讀(r)可寫(w)可執行(x)三種模式,分別針對該文件的擁有者( onwer )、同組用戶( group member )(可以 ls -lg來觀看某一文件的所屬的 group ),以及其他人( other )。


一個文件如果改成可執行模式則系統就將其視爲一個可執行文件,而一個目錄的可執行模式代表使用者有進入該目錄之權利。


chmod使用方式如下:


chmod [ -fR ] mode filename ...


其參數的意義如下:


-f Force. chmod 不會理會失敗的動作。 


-R Recurive. 會將目錄下所有子目錄及文件改爲你所要改成的模式。


關於 Process 處理的指令:
1. ps


ps 是用來顯示目前你的 process 或系統 processes 的狀況。


其選項說明如下:


-a 列出包括其他 users 的 process 狀況。


-u 顯示 user - oriented 的 process 狀況 。


-x 顯示包括沒有 terminal 控制的 process 狀況 。


-w 使用較寬的顯示模式來顯示 process 狀況 。


我們可以經由 ps 取得目前 processes 的狀況,如 pid , running state 等。


2. kill


kill 指令的用途是送一個 signal 給某一個 process 。因爲大部份送的都是用來殺掉 process 的 SIGKILL 或 SIGHUP,因此稱爲kill 。


kill 的用法爲:


kill [ -SIGNAL ] pid ...


kill -l


SIGNAL 爲一個 singal 的數字,從 0 到 31 ,其中 9 是 SIGKILL ,也就是一般用來殺掉一些無法正常 terminate 的信號。


也可以用 kill -l 來察看可代替 signal 號碼的數字。


關於字符串處理的指令:
1. echo


echo 是用來顯示一字符串在標準輸出上。echo -n 則表示當顯示完之後不執行換行操作。


2. grep


grep 爲一過濾器,它可自一個或多個文件中過濾出具有某個字符串的行,或是從標準輸入過濾出具有某個字符串的行。


 grep的用法如下:


grep [-nv] match_pattern file1 file2 ....


-n 把所找到的行在行前加上行號列出


-v 把不包含 match_pattern 的行列出


match_pattern 所要搜尋的字符串


-f 以 pattern_file 存放所要搜尋的字符串


聯機查詢的指令:
1. man


man 是手冊 ( manual ) 的意思。 UNIX 提供線上輔助( on-line help )的功能,man 就是用來讓使用者在使用時查詢指令、系統調用、標準庫函數、各種表格等的使用所用的。


man 的用法如下:


man [-M path] [[section] title ] .....


man [-M path] -k keyword ...


-M path man 所需要的 manual database 的路徑。我們也可以用設定環境變數 MANPATH 的方式來取代 -M 選項。


title 這是所要查詢的目標。 


section 用一個數字表示 manual 的分類,通常 1 代表可執行指令,2 代表系統調用( system call ) ,3 代表標準庫函數,等等。


 man 在 UNIX 上是一項非常重要的指令,我們在這裏所描述的用法僅僅只是一個大家比較常用的用法以及簡單的說明,真正詳細的用法與說明還是要通過使用 man 來得到。


2. who


who 指令是用來查詢目前有那些人在線上。


3. info


       info的查詢與man類似。


網絡運用指令:
1. telnet


telnet 是一個提供 user 通過網絡連到 remote host。


telnet 的 格式如下:


telnet [ hostname | ip-address ] [ port ]


hostname 爲一個像 ccsun1 或是 ccsun1.cc.nctu.edu.tw 的 name address,ip-address 則爲一個由四個小於 255 的數字組成的 ip address ,


2. ftp


ftp 的意義是 File Transfer Program ,是一個很常用的在網路文件傳輸的軟件。


ftp 的格式如下:


ftp [ hostname | ip-address ]


其中 hostname | ip-address 的意義跟 telnet 中的相同。


在進入 ftp 之後,如果與 remote host 連接上了,它將會詢問你 username 與密碼,如果輸入對了就可以開始進行文件傳輸。


在 ftp 中有許多的命令,這裏僅列出較常用的 cd , lcd , mkdir , put , mput , get , mget , binary , ascii , prompt , help 與 quit 的使用方式。


ascii 將傳輸模式設爲 ascii 模式。通常用於傳送文字文件。


binary 將傳輸模式設爲 binary 模式,通常用於傳送執行文件,壓縮文件與影像文件等。


cd remote-directory 將 remote host 上的工作目錄改變。


lcd [ directory ] 更改 local host 的工作目錄。


ls [ remote-directory ] [ local-file ] 列出 remote host 上的文件。


get remote-file [ local-file ] 取得登陸機的文件。


mget remote-files 可使用通用字符一次取得多個文件。


put local-file [ remote-file] 將 local host 的文件送到 remote host。


mput local-files 可使用通用字符一次將多個文件放到 remote host 上。


help [ command ] 線上輔助指令。


mkdir directory-name 在 remote host 新建一個目錄。


prompt 更改交談模式,若爲 on 則在 mput 與 mget 時每做一個文件傳輸時均會詢問。


Exit/quit離開ftp


3.rlogin命令


rlogin 是“remote login”(遠程登錄)的縮寫。該命令與telnet命令很相似,允許用戶啓動遠程系統上的交互命令會話。


rlogin 的一般格式是:


rlogin [ -8EKLdx ] [ -e char ] [-k realm ] [ - l username ] host


一般最常用的格式是:


rlogin host 


該命令中各選項的含義爲:


   -8 此選項始終允許8位輸入數據通道。該選項允許發送格式化的ANSI字符和其他的特殊代碼。如果不用這個選項,除非遠端的終止和啓動字符不是或,否則就去掉奇偶校驗位。


-E 停止把任何字符當作轉義字符。當和-8選項一起使用時,它提供一個完全的透明連接。


-K 關閉所有的Kerberos確認。只有與使用Kerberos 確認協議的主機連接時才使用這個選項。


-L 允許rlogin會話在litout模式中運行。要了解更多信息,請查閱tty聯機幫助。


-d 打開與遠程主機進行通信的TCP sockets的socket調試。要了解更多信息,請查閱setsockopt的聯機幫助。


-e 爲rlogin會話設置轉義字符,默認的轉義字符是“~”,用戶可以指定一個文字字符或一個\\nnn形式的八進制數。


-k 請求rlogin獲得在指定區域內的遠程主機的Kerberos許可,而不是獲得由krb_realmofhost(3)確定的遠程主機區域內的遠程主機的Kerberos 許可。


-x 爲所有通過rlogin會話傳送的數據打開DES加密。這會影響響應時間和CPU利用率,但是可以提高安全性。


4.rsh命令


rsh是“remote shell”(遠程 shell)的縮寫。 該命令在指定的遠程主機上啓動一個shell並執行用戶在rsh命令行中指定的命令。如果用戶沒有給出要執行的命令,rsh就用rlogin命令使用戶登錄到遠程機上。


rsh命令的一般格式是:


          rsh [-Kdnx] [-k realm] [-l username] host [command]


一般常用的格式是:


         rsh host [command ]


         command可以是從shell提示符下鍵人的任何Linux命令。


      rsh命令中各選項的含義如下:


    -K 關閉所有的Kerbero確認。該選項只在與使用Kerbero確認的主機連接時才使用。


    -d 打開與遠程主機進行通信的TCP sockets的socket調試。要了解更多的信息,請查閱setsockopt的聯機幫助。


    -k 請求rsh獲得在指定區域內的遠程主機的Kerberos許可,而不是獲得由krb_relmofhost(3)確定的遠程主機區域內的遠程主機的Kerberos許可。


    -l 缺省情況下,遠程用戶名與本地用戶名相同。本選項允許指定遠程用戶名,如果指定了遠程用戶名,則使用Kerberos 確認,與在rlogin命令中一樣。


    -n 重定向來自特殊設備/dev/null的輸入。


-x 爲傳送的所有數據打開DES加密。這會影響響應時間和CPU利用率,但是可以提高安全性。


Linux把標準輸入放入rsh命令中,並把它拷貝到要遠程執行的命令的標準輸入中。它把遠程命令的標準輸出拷貝到rsh的標準輸出中。它還把遠程標準錯誤拷貝到本地標準錯誤文件中。任何退出、中止和中斷信號都被送到遠程命令中。當遠程命令終止了,rsh也就終止了。


5.rcp命令


rcp代表“remote file copy”(遠程文件拷貝)。該命令用於在計算機之間拷貝文件。


rcp命令有兩種格式。第一種格式用於文件到文件的拷貝;第二種格式用於把文件或目錄拷貝到另一個目錄中。


   rcp命令的一般格式是:


            rcp [-px] [-k realm] file1 file2 rcp [-px] [-r] [-k realm] file


   directory 每個文件或目錄參數既可以是遠程文件名也可以是本地文件名。遠程文件名具有如下形式:rname@rhost:path,其中rname是遠程用戶名,rhost是遠程計算機名,path是這個文件的路徑。


   rcp命令的各選項含義如下:


   -r 遞歸地把源目錄中的所有內容拷貝到目的目錄中。要使用這個選項,目的必須是一個目錄。


     -p 試圖保留源文件的修改時間和模式,忽略umask。


   -k 請求rcp獲得在指定區域內的遠程主機的Kerberos 許可,而不是獲得由krb_relmofhost(3)確定的遠程主機區域內的遠程主機的Kerberos許可。


     -x 爲傳送的所有數據打開DES加密。這會影響響應時間和CPU利用率,但是可以提高安全性。 如果在文件名中指定的路徑不是完整的路徑名,那麼這個路徑被解釋爲相對遠程機上同名用戶的主目錄。如果沒有給出遠程用戶名,就使用當前用戶名。如果遠程機上的路徑包含特殊shell字符,需要用反斜線(\\)、雙引號(”)或單引號(’)括起來,使所有的shell元字符都能被遠程地解釋。 需要說明的是,rcp不提示輸入口令,它通過rsh命令來執行拷貝。


Vi常用技巧
l        取消命令


在vi中,只要沒有把修改結果存入磁盤文件中,那麼就可以通過“取消”來撤銷最近的操作或對緩衝區的修改。


假設你無意刪除了一行文本、改變了一些你不應該改變的內容或增加了一些不正確的文本,可以按<Esc>改變到命令模式中,然後按<u>,則文件內容恢復到修改前的樣子。


l        保存到文件名爲filename的文件中


發出寫命令格式:   :w filename


l        不使用小鍵盤來定位光標


vi用<h>、<j>、<k>、<l>鍵來定位光標。其中<h>、<l>鍵表示光標的左右移動,<j>、<k>鍵表示光標的上下移動,在某些沒有或不能使用小鍵盤的情況下這四個鍵是很有用的。


下面是其他一些用於移動光標的鍵:


n        按空格鍵或<l>向右移動光標一個位置


n        按<Return>將光標移動到下一行的行首


n        使用<j>鍵將光標移動到下一行的當前位置或行末


n        按<->將光標移動到上一行行首


n        使用<k>鍵將光標移動到上一行的當前位置或行末


n        按<h>將光標向左移動一個字符


n        按<0>(零)將光標移動到一行的行首


n        按<$>將光標移動到一行的行末


l        大範圍移動鍵


可快速定位光標到屏幕的頂部、中部和底部:


n        按<Shift-h>將光標移到屏幕的第一行,有時稱爲home位置


n        按<Shift-m>將光標移到現在屏幕顯示的各行的中間一行


n        按<Shift-l>將光標移到屏幕的最後一行


n        按<Ctrl-f>向前移動一屏


n        按<Ctrl-b>向後移動一屏


n        要移動到緩衝區中指定的行中,在按<Shift-g>前鍵入行號(注意,這裏的行號不是當前屏幕中的相對行號,而是絕對行號)


l        刪除文本


n        <x>刪除光標處的字符


n        <d> <w> 刪除從當前字的光標處到下一個字的開始處之間的內容


n        <d> <$> 刪除從光標處到行尾之間的內容


n        <Shift-d> 同<d> <$>,刪除當前行的剩餘部分


n        <d> <d> 刪除整行,不管光標在該行的位置


n        通過在上述命令之前鍵入一個整數,可將這些命令應用到幾個對象中,例如:<4> <x>刪除4個字符;<8> <d> <d> 刪除8行


l        添加文本


n        使用<i>在光標位置前插入文本


n        使用<Shift-i>使你進入輸入模式並且在當前行行首插入文本


n        使用<a>在光標位置後插入文本


n        使用<Shift-a>使你進入輸入模式並且在當前行末尾插入文本


n        使用<o>在當前行的下面打開一行以添加文本


n        使用<Shift-o>在當前行的上面打開一行以添加文本


l        使vi顯示行號


按<Esc>鍵以確保你在命令模式中,然後輸入:se number。要關閉行號,輸入:se nonumber


l        查找


n        /string     在緩衝區中向前查找字符串string


n        ?string    在緩衝區中向後查找字符串string


n        <n>        以當前的方向再次查找


n        <Shift-n>以相反的方向再次查找


n        注意,查找的字符串中若含有特殊字符的,要使用\來進行轉意


l        修改和替換文本


n        <r> 替換單個字符


n        <Shift-r>替換一個字符序列


n        <c> <w>修改當前字,從光標處到這個字的字尾


n        <c> <e>修改當前字,從光標處到這個字的字尾(與<c> <w>相同)


n        <c> <b>修改當前字,從該字的字頭到光標以前的那些字符


n        <c> <$>修改一行,從光標處到該行的行尾


n        <Shift-c>修改一行,從光標處到該行的行尾(與<c> <$>相同)


n        <c> <c>修改整行


n        注意,這些命令的每個命令都使之進入了輸入模式。除使用<r>來替換單個字符外,必須按<Esc>鍵來完成所作的修改並返回命令模式


n        要修改幾個字,在按<c> <w>之前使用一個整數


l        拷貝、剪切和粘貼


n        <y> <w>拷貝從當前字的光標處到下一個字的開始處之間的內容


n        <y> <$>拷貝從光標處到行尾之間的內容


n        <Shift-y>拷貝當前行的剩餘部分(與<y> <$>相同)


n        <y> <y>拷貝整個當前行


n        通過在這些命令前鍵入整數,所有這些命令都可以用於多個對象。


n        當刪除或剪切或拷貝時,刪除或拷貝的對象被保存在通用緩衝區中,可以使用<p>或<Shift-p>命令將這個緩衝區中的內容粘貼到光標位置。


n        <p>命令將對象粘貼到光標位置右邊或光標位置後面


n        <Shift-p>命令將對象粘貼到光標位置左邊或光標位置前面


l        重複命令

發佈了14 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章