1. 程式人生 > >Shell第二篇:正則表達式和文本處理工具

Shell第二篇:正則表達式和文本處理工具

chan 金字塔 文本 lar 乘法 res 每次 比較 而是

一 什麽是正則

  正則就是用一些具有特殊含義的符號組合到一起(稱為正則表達式)來描述字符或者字符串的方法。或者說:正則就是用來描述一類事物的規則。

生活中處處都是正則:

比如我們描述:4條腿

  你可能會想到的是四條腿的動物或者桌子,椅子等

繼續描述:4條腿,活的

就只剩下四條腿的動物這一類了

在linux中,通配符是由shell解釋的,而正則表達式則是由命令解釋的,下面我們就為大家介紹三種文本處理工具/命令:grep、sed、awk,它們三者均可以解釋正則。

二 grep

參數

-n :顯示行號
-o :只顯示匹配的內容
-q :靜默模式,沒有任何輸出,得用$?來判斷執行成功沒有,即有沒有過濾到想要的內容

-l :如果匹配成功,則只將文件名打印出來,失敗則不打印,通常-rl一起用,grep -rl ‘root‘ /etc
-A :如果匹配成功,則將匹配行及其後n行一起打印出來
-B :如果匹配成功,則將匹配行及其前n行一起打印出來
-C :如果匹配成功,則將匹配行及其前後n行一起打印出來
--color
-c :如果匹配成功,則將匹配到的行數打印出來
-E :等於egrep,擴展
-i :忽略大小寫

-v :取反,不匹配
-w:匹配單詞

技術分享
[[email protected] ~]# cat a.txt 
root123
ROot asdf
Root_123
rOOtss
root 123
[[email protected]
/* */ ~]# grep -i "root" a.txt root123 ROot asdf Root_123 rOOtss root 123 [[email protected] ~]# grep -w "root" a.txt root 123 技術分享

grep種類
grep
fgrep
pgrep
egrep

正則介紹

^ 行首
$ 行尾
. 除了換行符以外的任意單個字符
* 前導字符的零個或多個
.* 所有字符
[] 字符組內的任一字符
[^] 對字符組內的每個字符取反(不匹配字符組內的每個字符)
^[^] 非字符組內的字符開頭的行
[a-z] 小寫字母
[A-Z] 大寫字母
[a-Z] 小寫和大寫字母
[0-9] 數字
\< 單詞頭 單詞一般以空格或特殊字符做分隔,連續的字符串被當做單詞
\> 單詞尾

擴展正則 sed 加 -r 參數 或轉義
grep 加 -E 或 egrep 或轉義
AWK 直接支持 但不包含{n,m}
可以使用--posix支持
[[email protected] ~]# awk ‘/ro{1,3}/{print}‘ /etc/passwd
[[email protected] ~]# awk --posix ‘/ro{1,3}/{print}‘ /etc/passwd

sed -n ‘/roo\?/p‘ /etc/passwd
sed -rn ‘/roo?/p‘ /etc/passwd
? 前導字符零個或一個
+ 前導字符一個或多個
abc|def abc或def
a(bc|de)f abcf 或 adef
x\{m\} x出現m次
x\{m,\} x出現m次至多次(至少m次)
x\{m,n\} x出現m次至n次

posix定義的字符分類

[:alnum:] Alphanumeric characters.
匹配範圍為 [a-zA-Z0-9]
[:alpha:] Alphabetic characters.
匹配範圍為 [a-zA-Z]
[:blank:] Space or tab characters.
匹配範圍為 空格和TAB鍵
[:cntrl:] Control characters.
匹配控制鍵 例如 ^M 要按 ctrl+v 再按回車 才能輸出
[:digit:] Numeric characters.
匹配所有數字 [0-9]
[:graph:] Characters that are both printable and visible. (A space is print-
able, but not visible, while an a is both.)
匹配所有可見字符 但不包含空格和TAB 就是你在文本文檔中按鍵盤上能用眼睛觀察到的所有符號
[:lower:] Lower-case alphabetic characters.
小寫 [a-z]
[:print:] Printable characters (characters that are not control characters.)
匹配所有可見字符 包括空格和TAB
能打印到紙上的所有符號
[:punct:] Punctuation characters (characters that are not letter, digits, con-
trol characters, or space characters).
特殊輸入符號 +-=)(*&^%$#@!~`|\"‘{}[]:;?/>.<,
註意它不包含空格和TAB
這個集合不等於^[a-zA-Z0-9]
[:space:] Space characters (such as space, tab, and formfeed, to name a few).

[:upper:] Upper-case alphabetic characters.
大寫 [A-Z]
[:xdigit:] Characters that are hexadecimal digits.
16進制數 [0-f]

使用方法:
[[email protected] ~]# grep --color ‘[[:alnum:]]‘ /etc/passwd

三 sed

sed
流編輯器 stream editer,是以行為單位的處理程序

sed 流編輯器 stream editer

語法
sed [options] ‘command‘ in_file[s]
options 部分
-n
-e
-i
-f
command 部分
‘[地址1,地址2] [函數] [參數(標記)]‘

定址的方法 1.數字 2.正則
數字
十進制數
1 單行
1,3 範圍 從第一行到第三行
2,+4 匹配行後若幹行
4,~3 從第四行到下一個3的倍數行
2~3 第二行起每間隔三行的行
$ 尾行
1! 除了第一行以外的行
正則
正則必須用//包裹起來
擴展正則需要用 -r 參數或轉義

數字定址:sed -n ‘1p‘ /etc/passwd

正則定址:sed -n ‘/^root/p‘ /etc/passwd

正則介紹

^ 行首
$ 行尾
. 除了換行符以外的任意單個字符
* 前導字符的零個或多個
.* 所有字符
[] 字符組內的任一字符
[^] 對字符組內的每個字符取反(不匹配字符組內的每個字符)
^[^] 非字符組內的字符開頭的行
[a-z] 小寫字母
[A-Z] 大寫字母
[a-Z] 小寫和大寫字母
[0-9] 數字
\< 單詞頭 單詞一般以空格或特殊字符做分隔,連續的字符串被當做單詞
\> 單詞尾

擴展正則 加 -r 參數 或轉義
sed -n ‘/roo\?/p‘ /etc/passwd
sed -rn ‘/roo?/p‘ /etc/passwd
? 前導字符零個或一個
+ 前導字符一個或多個
abc|def abc或def
a(bc|de)f abcf 或 adef
x\{m\} x出現m次
x\{m,\} x出現m次至多次(至少m次)
x\{m,n\} x出現m次至n次


函數
增刪改
a 後插
c 替換
i 前插
d 刪除
輸入輸出
p 打印匹配的行 一般和 -n 參數連用,以屏蔽默認輸出
r 從文件中讀入
w 寫入到文件中
控制流
! 命令取反 例: 1!d 刪除第一行以外的行
{} 命令組合 命令用分號分隔 {1h;G} 可以理解為 -e 參數的另一種寫法

= 打印行號(輸入行的號碼,而非處理的次數行號) 例如: sed -n ‘2{=;p}‘ infile
n 讀入下一行到模式空間 例:‘4{n;d}‘ 刪除第5行
N 而是追加下一行到模式空間,再把當前行和下一行同時應用後面的命令

替換
s 字符串替換 s/old/new/

$ sed -n ‘s/root/ABCDEF/p‘ /etc/passwd

ABCDEF:x:0:0:root:/root:/bin/bash

operator:x:11:0:operator:/ABCDEF:/sbin/nologin

$ sed -n ‘s/root/ABCDEF/gp‘ /etc/passwd

ABCDEF:x:0:0:ABCDEF:/ABCDEF:/bin/bash

operator:x:11:0:operator:/ABCDEF:/sbin/nologin

$ sed -n ‘s/root/ABCDEF/2p‘ /etc/passwd

root:x:0:0:ABCDEF:/root:/bin/bash

$ sed -n ‘s/root/ABCDEF/3p‘ /etc/passwd

root:x:0:0:root:/ABCDEF:/bin/bash

$ sed -n ‘s/root/ABCDEF/gp‘ /etc/passwd

ABCDEF:x:0:0:ABCDEF:/ABCDEF:/bin/bash

operator:x:11:0:operator:/ABCDEF:/sbin/nologin

$

\(\) 保存被匹配的字符 以備反向引用\N時使用 最多9個標簽 標簽順序從左至右
& 替換時使用,在不定義標簽時使用(反向引用)


試做:

刪除第一個單詞

刪除最後一個單詞

將第一個單詞和最後一個單詞兌換位置


y 字符替換(變形)

工作模式 模式空間和保持空間介紹


$ sed ‘1{p;p}‘ a.txt

11111111

11111111

11111111

22222222

33333333

44444444

55555555

66666666

$


置換 模式空間和保持空間(暫存空間)
h 把模式空間內容覆蓋到保持空間中
H 把模式空間內容追加到保持空間中
g 把保持空間內容覆蓋到模式空間中
G 把保持空間內容追加到模式空間中
x 交換模式空間與保持空間的內容

# cat test.sh
1111111
2222222
3333333
4444444
# sed ‘{1h;2,3H;4G}‘ ./test.sh
1111111
2222222
3333333
4444444
1111111
2222222
3333333
# sed ‘{1h;2x;3g;$G}‘ ./test.sh
1111111
1111111
2222222
4444444
2222222
#


試做題

將第一行插入到每個偶數行的後面

$ sed ‘1h;0~2G‘ a.txt

11111111

22222222

11111111

33333333

44444444

11111111

55555555

66666666

11111111

$

顛倒輸出

$ sed ‘1!G;h;$!d‘ rev.txt

xyz

def

abc

$

腳本方法
-f 參數 引用腳本(腳本的末尾不能有空格制表符或其他文本)
# cat sed.sh
2,4d
s/777/seker/
s/999/seker&seker/
# sed -f sed.sh test.txt
1111111
5555555
6666666
seker7777
8888888
seker999seker9999
#

在腳本中指明解釋器為sed
# cat sed.sh
#!/bin/sed -f
2,4d
s/777/seker/
s/999/seker&seker/
# ./sed.sh test.txt
1111111
5555555
6666666
seker7777
8888888
seker999seker9999
#

高級流控命令 b分支 t測試
分支命令用於無條件轉移,測試命令用於有條件轉移

分支 branch
跳轉的位置與標簽相關聯
如果有標簽則跳轉到標簽所在的後面行繼續執行
如果沒有標簽則跳轉到腳本的結尾處.
標簽 以冒號開始後接標簽名 不要在標簽名前後使用空格
跳轉到標簽指定位置
[[email protected] ~]# grep seker /etc/passwd
seker:x:500:500::/home/seker:/bin/bash
[[email protected] ~]#
[[email protected] ~]# grep seker /etc/passwd |sed ‘:top;s/seker/blues/;/seker/b top;s/5/555/‘
blues:x:55500:500::/home/blues:/bin/bash
[[email protected] ~]#

命令分析:讓單次替換(cmd1)循環執行,直到條件不滿足
:top; 定義一個top標簽
s/seker/blues/; cmd1
/seker/b top; 如果模式匹配則跳轉到top標簽
s/5/555/ 當上一條模式不匹配時,既會繼續執行這一條

選擇執行
[[email protected] ~]# grep ‘seker‘ /etc/passwd |sed ‘s/seker/blues/;/seker/b end;s/5/555/;:end;s/5/666/‘
blues:x:66600:500::/home/seker:/bin/bash
[[email protected] ~]#

zorro:x:501:501::/home/zorro:/bin/bash
[[email protected] ~]# grep ‘zorro‘ /etc/passwd |sed ‘s/seker/blues/;/seker/b end;s/5/555/;:end;s/5/666/‘
zorro:x:6665501:501::/home/zorro:/bin/bash
[[email protected] ~]#

命令分析: 執行cmd1,再去模式匹配,成功則跳轉到cmd3開始執行,否則(模式不匹配)會按命令順序逐個執行
s/seker/blues/; cmd1
/seker/b end;
s/5/555/; cmd2
:end;
s/5/666/ cmd3

另一種選擇執行
[[email protected] ~]# grep ‘seker‘ /etc/passwd |sed ‘s/seker/blues/;/seker/b end;s/5/555/;b;:end;s/5/666/‘
blues:x:66600:500::/home/seker:/bin/bash

[[email protected] ~]# grep ‘zorro‘ /etc/passwd |sed ‘s/seker/blues/;/seker/b end;s/5/555/;b;:end;s/5/666/‘
zorro:x:55501:501::/home/zorro:/bin/bash
[[email protected] ~]#

命令分析: 執行cmd1;模式匹配cmd2成功則執行cmd3;否則執行cmd2,再跳轉到腳本末尾
s/seker/blues/; cmd1
/seker/b end;
s/5/555/; cmd2
b;
:end;
s/5/666/ cmd3


測試命令,如果前一個替換命令執行成功則跳轉到腳本末尾 (case結構)
[[email protected] ~]# grep ‘seker‘ /etc/passwd |sed ‘s/seker/ABC/;t;s/home/DEF/;t;s/bash/XYZ/‘
ABC:x:500:500::/home/seker:/bin/bash

[[email protected] ~]# grep ‘zorro‘ /etc/passwd |sed ‘s/seker/ABC/;t;s/home/DEF/;t;s/bash/XYZ/‘
zorro:x:501:501::/DEF/zorro:/bin/bash
[[email protected] ~]#

與標簽關聯,跳轉到標簽位置
[[email protected] ~]# grep ‘seker‘ /etc/passwd |sed ‘s/seker/ABC/;t end;s/home/DEF/;t;:end;s/bash/XYZ/‘
ABC:x:500:500::/home/seker:/bin/XYZ
[[email protected] ~]#

[[email protected] ~]$ grep ‘zorro‘ /etc/passwd |sed ‘s/seker/ABC/;t end;s/home/DEF/;t;:end;s/bash/XYZ/‘
zorro:x:501:501::/DEF/zorro:/bin/bash

四 awk

awk -F: ‘$1 == "root"{print $1,NR,NF}‘ /etc/passwd
awk -F: ‘NR>20 || NR<3{print $1,$3,$NF}‘ /etc/passwd
awk -F: ‘NR>1 && NR<3{print $1,NR,NF}‘ /etc/passwd
awk -F: ‘NR>1 && NR<4{print $1,NR,NF}‘ /etc/passwd
awk -F: ‘$1~/^r/{print $1,NR,NF}‘ /etc/passwd
awk -F: ‘/^root/{print $1,NR,NF}‘ /etc/passwd

username=root
awk -v var=$username -F: ‘$1 == var{print $1,NR,NF}‘ /etc/passwd

awk詳細

語法
awk [options] ‘commands‘ files
option
-F 定義字段分隔符,默認的分隔符是連續的空格或制表符
使用option中的-F參數定義間隔符號
用$1,$2,$3等的順序表示files中每行以間隔符號分隔的各列不同域
NF變量表示當前記錄的字段數
-v 定義變量並賦值 也可以借用次方式從shell變量中引入

command
讀前處理 行處理 讀後處理
1.讀前處理 BEGIN{awk_cmd1;awk_cmd2}
2.行處理:定址 命令
定址方法: 正則,變量,比較和關系運算
正則需要用//包圍起來
^ 行首
$ 行尾
. 除了換行符以外的任意單個字符
* 前導字符的零個或多個
.* 所有字符
[] 字符組內的任一字符
[^] 對字符組內的每個字符取反(不匹配字符組內的每個字符)
^[^] 非字符組內的字符開頭的行
[a-z] 小寫字母
[A-Z] 大寫字母
[a-Z] 小寫和大寫字母
[0-9] 數字
\< 單詞頭 單詞一般以空格或特殊字符做分隔,連續的字符串被當做單詞
\> 單詞尾

擴展正則 加 -r 參數 或轉義
sed -n ‘/roo\?/p‘ /etc/passwd
sed -rn ‘/roo?/p‘ /etc/passwd
? 前導字符零個或一個
+ 前導字符一個或多個
abc|def abc或def
a(bc|de)f abcf 或 adef
x\{m\} x出現m次
x\{m,\} x出現m次至多次(至少m次)
x\{m,n\} x出現m次至n次

NR變量定址
NR 表示AWK讀入的行數
FNR表示讀入行所在文件中的行數
# awk ‘{print NR,FNR,$1}‘ file1 file2
1 1 aaaaa
2 2 bbbbb
3 3 ccccc
4 1 dddddd
5 2 eeeeee
6 3 ffffff
#
邏輯運算 可直接引用域進行運算
== >= <= != > < ~ !~
# awk ‘NR==1 {print}‘ /etc/passwd
root:x:0:0:root:/root:/bin/bash
#
3.命令 {print $0}
4.讀後處理 END {awk_cmd1;awk_cmd2;}


AWK變量
NR 當前記錄的個數(全部文件連接後的統計)
FNR 當前記錄的個數(僅為當前文件的統計,非全部)
FS 字段分隔符 默認為連續空格或制表符,可以使用多個不同的符號做分隔符 -F[:/]
OFS 輸出字符的分隔符 默認是空格
# awk -F: ‘OFS="=====" {print $1,$2}‘ /etc/passwd
root=====x
NF 當前讀入行的字段個數
ORS 輸出記錄分隔符 默認是換行
# awk -F: ‘ORS="=====" {print $1,$2}‘ /etc/passwd
root x=====bin x=====
FILENAME 當前文件名

引用shell變量的方法
# a=root
# awk -v var=$a -F: ‘$1 == var {print $0}‘ /etc/passwd
或者 把整個命令拆開傳遞,讓shell變量外露,
# awk -F: ‘$1 == "‘$a‘" {print $0}‘ /etc/passwd
# a=NF
# awk -F: ‘{print $‘$a‘}‘ /etc/passwd

操作符
賦值
= += -= /= *=
邏輯與 邏輯或 邏輯非
&& || !
匹配正則或不匹配,正則需要用 /正則/ 包圍住
~ !~
關系 比較字符串時要把字符串用雙引號引起來
< <= > >= != ==
字段引用
$ 字段引用需要加$,而變量引用直接用變量名取
運算符
+ - * / % ++ --
轉義序列
\\ \自身
\$ 轉義$
\t 制表符
\b 退格符
\r 回車符
\n 換行符
\c 取消換行


練習
打印uid在30~40範圍內的用戶名。
打印第5-10行的行號和用戶名
打印奇數行
打印偶數行
打印字段數大於5的行
打印UID不等於GID的用戶名
打印沒有指定shell的用戶
打印1..1000以內的7的倍數和包含7的數

流程控制
分支結構

if (條件) 動作
若有多個動作,則要用大括號將動作體包含起來 if (條件) {動作1;動作2}
# awk -F: ‘{if ($1 == "root") print $1}‘ /etc/passwd
root
#
# awk -F: ‘{if ($1 == "root") {print $1;print $6}}‘ /etc/passwd
root
/root
#

if (條件1)
動作1
else
動作2
# awk -F: ‘{if ($1 == "root"){print $1}else print $6}‘ /etc/passwd
# awk -F: ‘{if ($1 == "root") print $1;else print $6}‘ /etc/passwd
上面兩個命令是等價的,要麽用分號隔開,表示第一個動作體的結束,要麽將動作體用大括號定位範圍

if (條件 1)
動作1
else if(條件 2)
動作2
else if(條件 3)
動作3
else
動作4
# awk -F: ‘{if ($1 == "root") print $1;else if ($1 == "seker") print $6;else if ($1 == "zorro") print $7;else print NR}‘ /etc/passwd
root
2
3
...
33
/home/seker
/bin/bash
36

條件 ? 動作1 : 動作2
expr?action1:action2
# awk -F: ‘var=($3 >= 500)?$1:"system_user" {print $1"\t"$3"\t"var}‘ /etc/passwd
# awk -F: ‘{print ($3>500?$1:$2)}‘ /etc/passwd

練習
將系統用戶按UID分組標記 0 admin; 1-499 sysuser; 500+ users
awk -F: ‘{if($3==0) print $1"\t"$3"\t""admin";else if($3>=1&&$3<500) print $1,$3,"sysuser";else print $1,$3,"user"}‘ /etc/passwd

輸出樣式
%s是字符類型,%d數值類型
printf默認是不輸出換行的所以要加\n
10和7是偏移量
默認是右對齊,所有加個- 就是左對齊,就是把不足的位數用空格填充
註意:格式與輸出列之間要有逗號
# awk -F: ‘{printf "%-10s %-10d %s\n",$1,$3,$7}‘ /etc/passwd


讀前處理和讀後處理
# awk -F: ‘BEGIN{i=1} {i++} END {print i}‘ /etc/passwd
47
#
# awk -F: ‘BEGIN {print NR,NF}‘ /etc/passwd
0 0
#
# awk -F: ‘END {print NR,NF}‘ /etc/passwd
46 7
#
練習
找出普通用戶的用戶名並統計數量
# awk -F: ‘BEGIN{i=0} $3 >= 500 {print $1;i++} END {print i}‘ /etc/passwd
計算UID相加的總和;計算GID相加的總和
# awk -F: ‘BEGIN{i=0}{sum+=$3;i++}END{print i;print sum}‘ /etc/passwd
# awk -F: ‘BEGIN{i=0}{sum+=$3;gsum+=$4;i++}END{print i;print sum;print gsum}‘ /etc/passwd
計算VSZ和RSS各自的和 並以M單位顯示
# ps aux | awk ‘BEGIN{i=0}NR>=2{sum+=$5;i++}END{print sum/1024"M"}‘
# ps aux | awk ‘BEGIN{i=0}NR>=2{vsum+=$5;rsum+=$6;i++}END{print vsum/1024"M";print rsum/1024"M";print i}‘
循環語句
while(條件) {
動作
條件運算
}
# awk -F: ‘{while($3<3) {print $3,$1;$3++}}‘ /etc/passwd
0 root
1 root
2 root
1 bin
2 bin
2 daemon
#
BEGIN塊可以獨立使用,不需要引入文件
# awk ‘BEGIN{i=1;while(i<100) {print i;i++}}‘
練習
打印100以內的偶數
# awk ‘BEGIN{i=1;while(i<100) {if (i%2==0) print i;i++}}‘

x=1
do {
動作1
x++
} while (x<5)
# awk ‘BEGIN{i=5;do{print i;i++}while(i<10)}‘
# awk ‘BEGIN{i=5;do{print i;i++}while(i<1)}‘

for(預置;條件;遞增) {
動作
}
# awk ‘BEGIN {for (x=1;x<=4;x++) print x }‘
1
2
3
4
#
# awk ‘BEGIN{for (i=1;i<=4;i++) {for (j=1;j<=4;j++) print i,j}}‘


練習
使用嵌套的for循環,打印100-999之間的數,個十百位分別用一個for來打印
# awk ‘BEGIN{OFS="";for (i=1;i<=9;i++) {for (j=0;j<=9;j++) {for (n=0;n<=9;n++) print i,j,n}}}‘
打印乘法口訣表
# cat 99.sh
#!/bin/bash
awk ‘BEGIN{
for(i=1;i<10;i++)
{
for(j=1;j<=i;j++)
printf "%d*%d=%d ",j,i,j*i
print
}

}‘
#

打印金字塔
# cat jin.sh
#!/bin/bash
awk ‘BEGIN{
num=5
for(i=1;i<=num;i++)
{
for (n=1;n<=num-i;n++)
printf "%s"," "
for (j=1;j<=2*i-1;j++)
printf "%s","*"
print
}
}‘
#

逆序輸出每個字段
達到這樣既可
/bin/bash
/root
root
0
0
x
root


# awk -F: ‘{for (x=NF;x>0;x--) print $x}‘ /etc/passwd

繼續解決上一個試做題的格式問題
# awk -F: ‘/bash$/{for (x=NF;x>0;x--) printf "%-13s",$x;printf "\n"}‘ /etc/passwd

跳轉語句
break 跳出循環
# awk ‘BEGIN {for(x=1;x<5;x++) {if (x==3) break;print x }}‘
1
2

continue 在達到循環底部之前終止當前循環 從新開始下一次循環
# awk ‘BEGIN {for(x=1;x<5;x++) {if (x==3) continue;print x }}‘
1
2
4

next 讀入下一行 同時返回腳本頂部 這樣可以避免對當前行執行其他操作
# awk -F: ‘NR > 5 {next} {print $1} END {print NR}‘ /etc/passwd
root
bin
daemon
adm
lp
46
#
exit 使讀取動作終止 並將控制移動到END,如果沒有END則終止腳本
# awk -F: ‘NR > 5 {exit} {print $1} END {print NR}‘ /etc/passwd
root
bin
daemon
adm
lp
6
#

數組
自定義數組
# awk ‘BEGIN {ary[1]="seker";ary[2]="zorro";print ary[1],ary[2]}‘
seker zorro
#
# awk ‘BEGIN {ary[1]="seker";ary[2]="zorro";for(i in ary) print ary[i]}‘
seker
zorro
#
刪除一個元素 對元素給空值並不能清除這個元素 要想清除一個元素需要使用delete ary[idx]
# awk ‘BEGIN {ary[1]="seker";ary[2]="zorro";ary[3]="blues";ary[2]="";for(i in ary) print ary[i]}‘
seker

blues
# awk ‘BEGIN {ary[1]="seker";ary[2]="zorro";ary[3]="blues";delete ary[2];for(i in ary) print ary[i]}‘
seker
blues
#

循環產生數組和取出數組
# awk ‘BEGIN{n=5;for (i=1;i<=n;i++) ary[i]=i+100;for(m in ary) print m,ary[m]}‘
4 104
5 105
1 101
2 102
3 103
#

# awk -F: ‘{ary[NR]=$1} END {for(i in ary) print i,ary[i]}‘ /etc/passwd
1 root
2 bin
3 daemon
4 adm
5 lp
6 sync
7 shutdown
8 halt
9 mail
# awk -F: ‘{ary[$3]=$1} END {for(i in ary) print i,ary[i]}‘ /etc/passwd
10 uucp
11 operator
12 games
13 gopher
14 ftp
32 rpc
37 rpm

ARGV 命令行中參數數組
# awk ‘{for (i in ARGV) {print i,ARGV[i]}}‘ /etc/passwd /etc/fstab
0 awk
1 /etc/passwd
2 /etc/fstab
#### i 為下標; ARGV[i] 下標為i的值
練習
統計每種shell被使用的次數


函數

算術函數 int
[[email protected] ~]# awk ‘BEGIN {print int(3.9415)}‘
3
[[email protected] ~]#


隨機數函數 rand() srand()
rand() 取值 0 > r < 1 之間 默認的種子是系統時間 精確到秒
srand()取值 0 > r < 1 之間 可以指定種子來影響rand()取值數 默認是系統時間 精確到秒

[[email protected] ~]# awk ‘BEGIN {srand(222);print int(rand()*100000000)}‘
90204196
[[email protected] ~]#

字符串函數
substr(s,x[,y])
返回字符串s中從位置x起至y的子串,如果沒有給出y,則從x開始到結束.
[[email protected] ~]# awk ‘BEGIN {x="abcdefxyz";print substr(x,4,3)}‘
def
[[email protected] ~]#

大寫小寫
sprintf() 本身並不能打印,做格式轉換,將數字轉換成ASCII字符
# awk ‘BEGIN {for(i=97;i<=122;++i) print tolower(toupper(sprintf("%c",i)))}‘

字符串長度
length() 如果沒有給定字符串則使用$0
[[email protected] ~]# awk ‘BEGIN {print length("abcdefxyz")}‘
9

gsub(/abc/,"ABC",x) 全局字符串替換
從x中用匹配的abc正則替換成ABC
[[email protected] ~]# awk ‘BEGIN {x="xyzabcxyzabcxyz";gsub(/abc/,"ABC",x);print x}‘
xyzABCxyzABCxyz
[[email protected] ~]# sub 第一次的替換
[[email protected] ~]# awk ‘BEGIN {x="xyzabcxyzabcxyz";sub(/abc/,"ABC",x);print x}‘
xyzABCxyzabcxyz
[[email protected] ~]#

gensub(r, s, h [, t]) Search the target string t for matches of the reg-
ular expression r. If h is a string beginning
with g or G, then replace all matches of r with s.
Otherwise, h is a number indicating which match of
r to replace. If t is not supplied, $0 is used
instead.
gensub(正則,替換,範圍,目標串)
[[email protected] ~]# awk ‘BEGIN{print gensub("zorro","AAAA","2","seker zorro zorro seker")}‘
seker zorro AAAA seker
[[email protected] ~]# echo seker zorro zorro seker | sed ‘s/zorro/AAAA/2‘
seker zorro AAAA seker
[[email protected] ~]#
[[email protected] ~]# echo seker zorro zorro seker | awk ‘{$0=gensub("zorro","AAAA","g");print}‘
seker AAAA AAAA seker
[[email protected] ~]# echo seker zorro zorro seker | awk ‘{$0=gensub("zorro","AAAA","2");print}‘
seker zorro AAAA seker
[[email protected] ~]# echo seker zorro zorro seker | awk ‘{$0=gensub("zorro","AAAA","h");print}‘
seker AAAA zorro seker
[[email protected] ~]# echo seker zorro zorro seker | awk ‘{$0=gensub("zorro","AAAA","1");print}‘
seker AAAA zorro seker
[[email protected] ~]#


系統函數
getline

交互輸入
[[email protected] ~]# awk -F: ‘BEGIN {printf "Enter Number: ";getline ;for(i=1;i<=$0;i++) print i}‘
Enter Number: 3
1
2
3
[[email protected] ~]#

將輸入賦值給變量
[[email protected] ~]# awk -F: ‘BEGIN {printf "Enter Number: ";getline NUM;for(i=1;i<=NUM;i++) print i}‘
Enter Number: 3
1
2
3
[[email protected] ~]#

從文件中讀入
[[email protected] ~]# awk -F: ‘BEGIN {getline < "/etc/passwd" ; print $3"\t"$1}‘
0 root
[[email protected] ~]#

#awk -F: ‘BEGIN {while (getline < "/etc/passwd" > 0) print $3"\t"$1}‘

getline < "/etc/passwd" 從文件中讀入,每次讀取一行,默認情況下讀取的次數等於awk自身引入文件的行數
也可以放到for中來控制讀取的次數
> 0 測試讀取的返回值,成功返回1,失敗返回-1,0文件末尾

從命令輸出中輸入
[[email protected] ~]# awk ‘BEGIN {"uname -a"|getline ;print $3}‘
2.6.18-53.el5
[[email protected] ~]#


system(command)
系統命令要用""引起來
[[email protected] ~]# rm -rf abc/
[[email protected] ~]# awk ‘BEGIN {if(system("mkdir abc") != 0 ) print "ERR"}‘
[[email protected] ~]# awk ‘BEGIN {if(system("mkdir abc") != 0 ) print "ERR"}‘
mkdir: 無法創建目錄 “abc”: 文件已存在
ERR
[[email protected] ~]#
[[email protected] ~]# awk ‘BEGIN {if(system("mkdir abc 2>/dev/null") != 0 ) print "ERR"}‘
ERR
[[email protected] ~]#

awk腳本的介紹 -f 與 #!/bin/awk -f

使用awk添加系統用戶
[[email protected] ~]# cat useradd.awk
#!/bin/awk -f

{
system("useradd "$1";echo "$2"|passwd --stdin "$1)
}
[[email protected] ~]# cat username
myname 1234
[[email protected] ~]#
[[email protected] ~]# ./useradd.awk ./username
Changing password for user myname.
passwd: all authentication tokens updated successfully.
[[email protected] ~]#

五 其他補充

[[email protected] ~]# cat test |sort |uniq #排序去重
11111111111
2222222222
333333333
4444444444
55555555555
6666666666
777777777777
888888888888
99999999999999
aaaaaaaaaa


[[email protected] ~]# cat /etc/passwd |cut -d: -f2

[[email protected] ~]# find / -size +2M -type f -name \*.txt

Shell第二篇:正則表達式和文本處理工具