02-01 正則表達式和文本處理工具


一 什么是正則

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

生活中處處都是正則:

    比如我們描述: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:匹配單詞

復制代碼
[root@MiWiFi-R3-srv ~]# cat a.txt 
root123
ROot asdf
Root_123
rOOtss
root 123
[root@MiWiFi-R3-srv ~]# grep -i "root" a.txt 
root123
ROot asdf
Root_123
rOOtss
root 123
[root@MiWiFi-R3-srv ~]# 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支持
[root@MiWiFi-R3-srv ~]#  awk '/ro{1,3}/{print}' /etc/passwd
[root@MiWiFi-R3-srv ~]#  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]

使用方法:
[root@seker ~]# grep --color '[[:alnum:]]' /etc/passwd

正則表達式及字符處理
目標文件/etc/passwd,使用grep命令或egrep
1.顯示出所有含有root的行:
2.輸出任何包含bash的所有行,還要輸出緊接着這行的上下各兩行的內容:
3.  顯示出有多少行含有nologin。
4.顯示出那些行含有root,並將行號一塊輸出。
5.顯示出文件中
6.新建用戶
    abominable
    abominate
    anomie
    atomize
    編寫正則表達式,將他們匹配出來
    egrep 'a.omi(nabl|nat|z|)e' /etc/passwd
7.建四個用戶
    Alex213sb
    Wpq2222b
    yH438PIG
    egon666
    egon

    過濾出用戶名組成是字母+數字+字母的行
[root@MiWiFi-R3-srv ~]# egrep '^[a-Z]+[0-9]+[a-Z]+' /etc/passwd
8.顯示出/etc目錄下所有包含root的文件名
9. 過濾掉/etc/ssh/sshd_config內所有注釋和所有空行
grep -v '^#' /etc/ssh/sshd_config |grep -v '^ *$'
grep作業

三 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
跳轉的位置與標簽相關聯
如果有標簽則跳轉到標簽所在的后面行繼續執行
如果沒有標簽則跳轉到腳本的結尾處.
標簽 以冒號開始后接標簽名 不要在標簽名前后使用空格
跳轉到標簽指定位置
[root@stu254 ~]# grep seker /etc/passwd
seker:x:500:500::/home/seker:/bin/bash
[root@stu254 ~]#
[root@stu254 ~]# grep seker /etc/passwd |sed ':top;s/seker/blues/;/seker/b top;s/5/555/'
blues:x:55500:500::/home/blues:/bin/bash
[root@stu254 ~]#

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

選擇執行
[root@stu254 ~]# 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
[root@stu254 ~]#

zorro:x:501:501::/home/zorro:/bin/bash
[root@stu254 ~]# 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
[root@stu254 ~]#

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

另一種選擇執行
[root@stu254 ~]# 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

[root@stu254 ~]# 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
[root@stu254 ~]#

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


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

[root@stu254 ~]# grep 'zorro' /etc/passwd |sed 's/seker/ABC/;t;s/home/DEF/;t;s/bash/XYZ/'
zorro:x:501:501::/DEF/zorro:/bin/bash
[root@stu254 ~]#

與標簽關聯,跳轉到標簽位置
[root@stu254 ~]# 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
[root@stu254 ~]#

[seker@seker ~]$ 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

Sed作業:以/etc/passwd文件為模板

1,刪除文件每行的第一個字符。

2,刪除文件每行的第二個字符。

3,刪除文件每行的最后一個字符。

4,刪除文件每行的倒數第二個字符。

5,刪除文件每行的第二個單詞。

6,刪除文件每行的倒數第二個單詞。

7,刪除文件每行的最后一個單詞。

8,交換每行的第一個字符和第二個字符。

9,交換每行的第一個字符和第二個單詞。

10,交換每行的第一個單詞和最后一個單詞。

11,刪除一個文件中所有的數字。

12,刪除每行開頭的所有空格。

13,用制表符替換文件中出現的所有空格。

14,把所有大寫字母用括號()括起來。

15,打印每行3次。

16,隔行刪除。

17,把文件從第2行到第5行復制到第7行后面。(選做題)

18,把文件從第2行到第5行移動到第7行后面。(選做題)

19,只顯示每行的第一個單詞。

20,打印每行的第一個單詞和第三個單詞。

21,將格式為    mm/yy/dd    的日期格式換成   mm;yy;dd

22, a.txt內容
    ABC
    DEF
    XYZ
    通過SED實現tac命令
    tac a.txt
    XYZ
    DEF
    ABC

1. sed -r 's/^(.)(.*)/\2/' /etc/passwd
2. sed -r 's/^(.)(.)(.*)/\1\3/' /etc/passwd
3. sed -r 's/(.*)(.)$/\1/' /etc/passwd
4. sed -r 's/(.*)(.)(.)$/\1\3/' /etc/passwd
5. sed -r 's/^([a-Z]+)([^a-Z]+)([a-Z]+)([^a-Z]+)/\1\2\4/' /etc/passwd
6. sed -r 's/([^a-Z]+)([a-Z]+)([^a-Z]+)([a-Z]+)$/\1\3\4/' /etc/passwd
7. sed -r 's/([^a-Z]+)([a-Z]+)([^a-Z]+)([a-Z]+)$/\1\2\3/' /etc/passwd
8. sed -r 's/^(.)(.*)(.)$/\3\2\1/' /etc/passwd
9. sed -r 's/^(.)([a-Z]+)([^a-Z]+)([a-Z]+)([^a-Z]+)/\4\2\3\1\5/' /etc/passwd
10. sed -r 's/^([a-Z]+)([^a-Z]+)(.*)([^a-Z]+)([a-Z]+)$/\5\2\3\4\1/' /etc/passwd
11. sed -r 's/[0-9]//g' /etc/passwd
12. sed -r 's/^ *//g' /etc/passwd
13. sed -r 's/ /\t/g' /etc/passwd
14. sed -r 's/[A-Z]/(&)/g' /etc/passwd
15. sed 'p;p' /etc/passwd
16. sed '1~2d' /etc/passwd

選做題17-18:文件內容
[root@MiWiFi-R3-srv ~]# cat test 
11111111111
2222222222
333333333
4444444444
55555555555
6666666666
777777777777
888888888888
99999999999999

17.sed '2h;3,5H;7G' test 

18. sed '2h;3,5H;2,5d;7G' test 

19. sed -r 's/^([a-Z]+)([^a-Z]+)(.*)/\1/' /etc/passwd
20. sed -r 's/^([a-Z]+)([^a-Z]+)([a-Z]+)([^a-Z]+)([a-Z]+)([^a-Z]+)/\5\2\3\4\1\6/' /etc/passwd
21. 
22. sed -r '{1h;1d;2G;2h;2d;3G;3h}'
23. 
echo "2012/12/11" |sed -r 's/\//:/g'
echo "2012/12/11" |sed -r 's#\/#:#g'
echo "2012/12/11" |sed -r 's@\/@:@g'
sed作業

四 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

打印uid在30~40范圍內的用戶名。
打印第5-10行的行號和用戶名
打印奇數行
打印偶數行
打印字段數大於5的行
打印UID不等於GID的用戶名
打印沒有指定shell的用戶
awk作業

 

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
[root@stu254 ~]# awk 'BEGIN {print int(3.9415)}'
3
[root@stu254 ~]#

 


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

 

[root@stu254 ~]# awk 'BEGIN {srand(222);print int(rand()*100000000)}'
90204196
[root@stu254 ~]#

 

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

 

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

 

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

 

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

 

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(正則,替換,范圍,目標串)
[root@tch254 ~]# awk 'BEGIN{print gensub("zorro","AAAA","2","seker zorro zorro seker")}'
seker zorro AAAA seker
[root@tch254 ~]# echo seker zorro zorro seker | sed 's/zorro/AAAA/2'
seker zorro AAAA seker
[root@tch254 ~]#
[root@tch254 ~]# echo seker zorro zorro seker | awk '{$0=gensub("zorro","AAAA","g");print}'
seker AAAA AAAA seker
[root@tch254 ~]# echo seker zorro zorro seker | awk '{$0=gensub("zorro","AAAA","2");print}'
seker zorro AAAA seker
[root@tch254 ~]# echo seker zorro zorro seker | awk '{$0=gensub("zorro","AAAA","h");print}'
seker AAAA zorro seker
[root@tch254 ~]# echo seker zorro zorro seker | awk '{$0=gensub("zorro","AAAA","1");print}'
seker AAAA zorro seker
[root@tch254 ~]#

 


系統函數
getline

 

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

 

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

 

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

 

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

 

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

 

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

 

 

 


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

 

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

 

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

 

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

 

1.使用:或/符號做分隔符,將字段逆序輸出/etc/passwd文件的每行

2.
# awk -F: 'NR < 11 {print $3,$1}' /etc/passwd > name.txt
# awk -F: 'NR < 11 {print $3,$6}' /etc/passwd > home.txt
# join name.txt home.txt 
觀察兩個文件,以及join命令輸出,用awk引入name.txt,home.txt兩個文件,模擬joni命令的輸出

3.統計/etc/passwd中每種shell的被使用人數
輸出格式:
counts    shell
1    
1    /bin/sync
4    /bin/bash
31    /sbin/nologin
1    /sbin/halt
1    /sbin/shutdown

4.統計ps中RSZ,VSS各自總和
輸出格式:
ps MEM statistic
VSZ_SUM : 164.277M
RSS_SUM : 47.8555M


5.計算/etc/passwd中所有用戶的UID平均數,以及GID平均數.
輸出格式:
UID and GID AVG
UID-AVG : 1750.72
GID_AVG : 1754

6.
根據uid值給用戶分等級 Admin system users
輸出格式:

LEVEL  NAME

Admin    root
sysuser    bin
users    seker

admin_count: N sys_user_count: N users_count: N 

7.
分別用GREP,SED,AWK將ifconfig中匹配到eth1的網卡所有信息打印出來.

8.
SHELL實現批量建立多個文件,將文件拓展名加上.txt,再加上.doc,再把中間的.txt去掉
9.
AWK腳本實現間隔五行打印表頭
[root@mail ~]# ./awk_print.sh /etc/passwd
Username  Uid    
root      0      
bin       1      
daemon    2      
adm       3      
lp        4      

Username  Uid    
shutdown  6      
halt      7      
mail      8      
news      9      
uucp      10     


10.用$RANDOM產生100個隨機數,交給AWK產生數組,在數組內部排序,最后輸出.(禁止用sort命令)

11. 建立 aa.txt 文本
# cat aa.txt 
aaa/bbb/ccc
ddd/eee/fff
ggg/hhh/iii
aaa/bbb/ccc/aaa/bbb/ccc/ddd/eee
# 用AWK或SED輸出如下格式
ccc aaa/bbb/
fff ddd/eee/
iii ggg/hhh/
eee aaa/bbb/ccc/aaa/bbb/ccc/ddd/



12. 建立 a.txt b.txt 文件
[root@tch254 ~]# cat a.txt 
1    a
2    b
3    c
4    d
5    e
6    f
7    g
[root@tch254 ~]# cat b.txt 
5    ABC
3    DEF
4    XYZ
[root@tch254 ~]# 使用AWK處理兩個文件輸出如下結果
e ABC
c DEF
d XYZ
[root@tch254 ~]# 
awk選做作業

五 其他補充

11111111111
2222222222
333333333
4444444444
55555555555
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
99999999999999
aaaaaaaaaa
6666666666
777777777777
888888888888
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
99999999999999
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
test文件內容

 

[root@MiWiFi-R3-srv ~]# cat test |sort |uniq #排序去重
11111111111
2222222222
333333333
4444444444
55555555555
6666666666
777777777777
888888888888
99999999999999
aaaaaaaaaa


[root@MiWiFi-R3-srv ~]# cat /etc/passwd |cut -d: -f2

[root@MiWiFi-R3-srv ~]# find / -size +2M -type f -name \*.txt

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM