Shell第二篇:正規表示式和文字處理工具

linhaifeng發表於2017-03-21

一 什麼是正則

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

生活中處處都是正則:

    比如我們描述: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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

相關文章