AWK命令和SED命令

加班的日子也要抓緊時間學習哇🤩

爲何要用

對於咱們後端人員來講,常常須要去服務器查找日誌信息,排查詳細錯誤信息或者監控服務器,強大如grep已經能夠知足絕大部分需求,可是awk和sed這兩個強大的命令工具也很好用,下面記錄一下這兩個工具如何使用。html


AWK

引自百科:mysql

AWK是一個優良的文本處理工具,Linux及Unix環境中現有的功能最強大的數據處理引擎之一。這種編程及數據操做語言(其名稱得自於它的創始人阿爾佛雷德·艾侯、彼得·溫伯格和布萊恩·柯林漢姓氏的首個字母)的最大功能取決於一我的所擁有的知識。awk通過改進生成的新的版本nawk,gawk,如今默認linux系統下平常使用的是gawk,用命令能夠查看正在應用的awk的來源(ls -l /bin/awk )linux


開始使用

先查詢一份網絡數據,使用重定向保存下來:正則表達式

$ netstat -ano > netstat.txt
$ awk '{print}' netstat.txt
Proto Recv-Q Send-Q Local Address           Foreign Address         State       Timer
tcp        0      0 0.0.0.0:8009            0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:18090           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:41999           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:42801           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:42869           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:10050           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 127.0.0.1:8005          0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0.0.0.0:10050        192.168.120.1:7569      TIME_WAIT   timewait (27.08/0/0)
複製代碼

簡單使用,輸出第1列和第4列:sql

  • 其中單引號中的被大括號括起來的就是awk的語句,注意,其只能被單引號包含。
  • 其中的1....n表示第幾列。($0表示整個行)
$ awk '{print $1, $4}' netstat.txt
Proto Local
tcp 0.0.0.0:8009
tcp 0.0.0.0:18090
tcp 0.0.0.0:41999
tcp 0.0.0.0:8080
tcp 0.0.0.0:42801
tcp 0.0.0.0:42869
tcp 0.0.0.0:22
tcp 0.0.0.0:10050
tcp 127.0.0.1:8005
複製代碼

awk能夠格式化輸出,用過C和Java等語系的應該都挺熟悉:shell

$ awk '{printf "%-8s %-8s %-8s %-18s %-22s %-15s\n",$1,$2,$3,$4,$5,$6}' netstat.txt
Proto    Recv-Q   Send-Q   Local              Address                Foreign
tcp      0        0        0.0.0.0:8009       0.0.0.0:*              LISTEN
tcp      0        0        0.0.0.0:18090      0.0.0.0:*              LISTEN
tcp      0        0        0.0.0.0:41999      0.0.0.0:*              LISTEN
tcp      0        0        0.0.0.0:8080       0.0.0.0:*              LISTEN
複製代碼

過濾記錄

能夠對其中某個或某些字段進行判斷,使用比較判斷符便可(經過與或非進行鏈接)apache

比較判斷符:==、!=、>=、>=、>、< 鏈接符:|| 、&&、 !編程

例如篩選出第三列大於0的數據行:vim

$ awk '$3 > 0 {print }' netstat.txt
Proto Recv-Q Send-Q Local Address           Foreign Address         State       Timer
tcp        0     48 10.96.0.33:22           0.0.0.0:42280       ESTABLISHED on (0.20/0/0)
複製代碼

若是須要打印行號的話,可使用內建變量NR:後端

$ awk '$3 > 0 {print NR, $0 }' netstat.txt
1 Proto Recv-Q Send-Q Local Address           Foreign Address         State       Timer
23 tcp        0         0.0.0.0:22            0.0.0.0:42280     ESTABLISHED on (0.20/0/0)
複製代碼

內建變量

內建變量至關於該程序的內置變量,能夠直接拿來使用:

變量 變量含義
$0 當前記錄(這個變量存放當前行的內容
1~n 當前記錄的第n個字段,字段由分隔符FS分割
FS 輸入字段分隔符,默認是空格或Tab
NF 當前記錄中的字段個數,就是有多少列
NR 已經讀出的記錄數,就是行號,從1開始,若是有多個文件的話,這個值也是不斷累加中
FNR 當前記錄數,與NR不一樣的是,這個值是各自文件本身的行號
RS 輸入的記錄分隔符,默認是換行符
OFS 輸出字段分隔符,默認也是空格
ORS 輸出的記錄分隔符,默認是換行符
FILENAME 當前輸入文件的名字

例若有些文件的分隔符不是空格,而是其餘,能夠自定義分隔符:

$ awk 'BEGIN{FS=":"} {print $1, $3, $6}' /etc/passwd
nobody -2 /var/empty
root 0 /var/root
複製代碼

上面命令等價於:(-F的意思就是制定分隔符)

$ awk -F: '{print $1, $3, $6}' /etc/passwd
複製代碼

注意:若是要制定多個分隔符,可使用:

awk -F '[;:]'

自定義輸出字段的分隔符(例如製表符\t):

$ awk 'BEGIN{FS=":"} {print $1, $3, $6}' OFS="\t" /etc/passwd
nobody	-2	/var/empty
root	0	/var/root
daemon	1	/var/root
複製代碼

字符串匹配

能夠跟grep同樣,匹配相關字符:

$ awk '/LISTEN|WAIT/' netstat.txt
tcp        0      0 0.0.0.0:8009            0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:18090           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:41999           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:42801           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:42869           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:10050           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 127.0.0.1:8005          0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:10050           0.0.0.0:7569      TIME_WAIT   timewait (27.08/0/0)
複製代碼

還能夠精確匹配某個字段:

$ awk '$6 ~/LISTEN|CLOSE/ {print $0}' netstat.txt
tcp        0      0 0.0.0.0:8009            0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:18090           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:41999           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:42801           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:42869           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:10050           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 127.0.0.1:8005          0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        1      0 0.0.0.0:44690           0.0.0.0:80            CLOSE_WAIT  off (0.00/0/0)
tcp6       0      0 :::27777                :::*                    LISTEN      off (0.00/0/0)
tcp6       0      0 :::10050                :::*                    LISTEN      off (0.00/0/0)
複製代碼

其中:~表示模式開始,兩個/中間是模式,至關於進行正則表達式的匹配(一樣道理,在正則表達式前使用!能夠進行取反操做,這裏就不展現了)


拆分文件

指定某列爲分類符,使用重定向就能夠導出到不一樣的文件中,例以下列語句經過第六列進行分割:

$ awk 'NR!=1 {print > $6}' netstat.txt
$ ls
ESTABLISHED          TIME_WAIT            netstat.txt          test.txt     CLOSE_WAIT           LISTEN               off
複製代碼

能夠查看各自文件,發現已是分類後的結果:

$ cat ESTABLISHED
tcp        0      0 0.0.0.0:38440        0.0.0.0:20880       ESTABLISHED keepalive (5806.94/0/0)
tcp        0      0 0.0.0.0:58814        0.0.0.0:20880        ESTABLISHED keepalive (5872.47/0/0)
tcp        0      0 0.0.0.0:49998        0.0.0.0:80        ESTABLISHED off (0.00/0/0)
tcp        0      0.0.0.03:22            0.0.0.02:42280    ESTABLISHED on (0.20/0/0)
tcp        0      0 0.0.0.0:56146        0.0.0.0:20892    ESTABLISHED keepalive (2661.21/0/0)
複製代碼

一樣,能夠指定某些列進行輸出,也可使用複雜的表達式(例如if-else-if語句,awk是個腳本解釋器)

$ awk 'NR!=1{if($6 ~ /TIME|ESTABLISHED/) print > "1.txt";
else if($6 ~ /LISTEN/) print > "2.txt";
else print > "3.txt" }' netstat.txt
$ ls *.txt
1.txt       2.txt       3.txt       netstat.txt
複製代碼

注意,if-else語句要在同一個花括號{}裏~


統計

下面語句是用來統計以.txt爲後綴的文件總大小:

$ ll *.txt | awk '{sum+=$5} END {print sum}'
769.9
複製代碼

一樣,能夠在統計時使用數據,分開統計不一樣項目的總數

用來統計網絡狀態

$ awk 'NR!=1{a[$6]+=1} END {for (i in a) print i ", " a[i];}' netstat.txt
LISTEN, 11
CLOSE_WAIT, 1
TIME_WAIT, 26
off, 7
ESTABLISHED, 10
複製代碼

用來統計每一個用戶的進程佔了多少內存:(經過ps -aux查看,第六列表示佔用的內存)

$ ps -aux | awk 'NR!=1{a[$1]+=$6} END {for (i in a) print i ", " a[i]"kb";}'
apache, 156952kb
dbus, 1236kb
polkitd, 8252kb
named, 104220kb
libstor+, 148kb
mysql, 106304kb
root, 325464kb
複製代碼

awk腳本

有兩個關鍵字須要注意BEGIN和END

  • BEGIN{這裏放的是,執行前的語句}
  • END{這裏放的是,執行後的語句}
  • {這裏放的是處理每一行時要執行的語句}

例以下面例子,簡單統計行數:(只是簡單介紹一下腳本如何寫,更多定製化的能夠小夥伴去探索~)

$ vim cal.awk
#! /bin/awk -f
# 運行前
BEGIN {
    lineNumber = 0
    print "開始執行"
}
# 運行中
{
    lineNumber += 1
    printf "當前行號 %s, 數據爲 %s \n", lineNumber, $0

}
# 運行後
END {
    print "結束"
}
複製代碼

執行腳本(也可使用./cal.awk netstat.txt)

$ awk -f cal.awk netstat.txt
awk -f cal.awk netstat.txt
開始執行
當前行號 1, 數據爲 Proto Recv-Q Send-Q Local Address           Foreign Address         State       Timer
當前行號 2, 數據爲 tcp        0      0 0.0.0.0:8009            0.0.0.0:*               LISTEN      off (0.00/0/0)
當前行號 3, 數據爲 tcp        0      0 0.0.0.0:18090           0.0.0.0:*               LISTEN      off (0.00/0/0)
當前行號 4, 數據爲 tcp        0      0 0.0.0.0:41999           0.0.0.0:*               LISTEN      off (0.00/0/0)
當前行號 5, 數據爲 tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      off (0.00/0/0)
當前行號 6, 數據爲 tcp        0      0 0.0.0.0:42801           0.0.0.0:*               LISTEN      off (0.00/0/0)
當前行號 7, 數據爲 tcp        0      0 0.0.0.0:42869           0.0.0.0:*               LISTEN      off (0.00/0/0)
結束
複製代碼

結合環境變量

經過使用-v參數和ENVIRON,與環境變量打交道:

$ echo $x
0
$ echo ${test}
LISTEN
$ awk -v val=${x} '$2==val || $6==ENVIRON["test"] { print $1, $2, $3}' netstat.txt
tcp 0 0
tcp 0 0
tcp 0 0
tcp 0 0
tcp 0 0
複製代碼

固然,在使用環境變量的時候,記得要export該變量


SED命令

全稱是:Stream EDitor,功能月awk相似,差異在於,sed比awk簡單一點,經常使用於字符替換。匹配符可使用正則表達式進行匹配,因此想要用更強大的功能,須要去了解一下正則表達式~


使用S命令替換

使用如下文本進行測試

$ cat self.txt
First line
    My name is John
Second line
    I like milk
Third line
    I like basketball
Four line
    I live in china
複製代碼

S命令介紹:

s表示替換命令,/I/表示匹配I, /John/表示將前面的I替換成Json,/g表示進行全部行上全部字符的匹配(注意,匹配時區分大小寫)

$ sed "s/I/John/g" self.txt
First line
    My name is John
Second line
    John like milk
Third line
    John like basketball
Four line
    John live in china
複製代碼

這樣只是對於輸出流的字符進行替換,原文本的數據不會改變,有兩種方法能夠修改輸出流的數據:

  • 使用重定向 >
$ sed "s/I/John/g" self.txt > other.txt
複製代碼
  • 使用-i參數
$ sed -i "s/I/John/g" self.txt
複製代碼

能夠在每一行最前面加數據:(例如將文本開頭加個#字符)

$ sed "s/^/#/g" self.txt
sed "s/^/#/g" self.txt
#First line
# My name is John
#Second line
# I like milk
#Third line
# I like basketball
#Four line
# I live in china
複製代碼

能夠在行末尾加一些數據:(例如加分割符或者結束符)

$ sed "s/$/---/g" self.txt
First line---
    My name is John---
Second line---
    I like milk---
Third line---
    I like basketball---
Four line---
    I live in china---
複製代碼

使用正則表達式去掉html中的tags:

Html文本爲:

<b>This is</b><p> a test file</p><span style="text-decoration: underline;"> @John hahah</span>
複製代碼

替換腳本:

# 熟悉正則表達式的小夥伴應該不陌生
# 使用[^>]表示除了>以外的字符,*號表示任意個字符
$ sed "s/<[^>]*>//g" html.txt
This is a test file @John hahah
複製代碼

能夠指定替換特定行號的文本,多行間使用逗號(,)進行鏈接:(例以下面只替換第5到第8行的數據)

$ sed "5,8s/I/John/g" self.txt
First line
    My name is John
Second line
    I like milk             #這行的I沒有被替換
Third line
    John like basketball     #替換了
Four line
    John live in china       #替換了
複製代碼

測試文本:

$ cat 1.txt
This is first line.
This is second line.
This is third line.
This is four line.
複製代碼

指定只替換每一行第一個出現的字符:

$ sed "s/s/S/1" 1.txt
ThiS is first line.
ThiS is second line.
ThiS is third line.
ThiS is four line.
複製代碼

指定替換每一行第二個及第二個以後的字符:

$ sed "s/s/S/2g" 1.txt
This iS firSt line.
This iS Second line.
This iS third line.
This iS four line.
複製代碼

多個匹配

若是須要在一次命令中,匹配多個模式,能夠寫多個匹配表達式,經過分號;進行分割:(例以下面將1,2行的This替換成That,3到最後一行的is替換成are)

$ sed '1,2s/This/That/g; 3,$s/is/are/g' 1.txt
That is first line.
That is second line.
Thare are third line.   #沒想到將This的is也替換成are了=-=
Thare are four line.    #能夠修改sed '1,2s/This/That/g; 3,$s/is/are/2' 1.txt, 指定替換第二個字符is
複製代碼

可使用&,用來當作被匹配的變量,而後在匹配的變量先後加一些內容:

$ sed 's/line/front [&] end/g' 1.txt
This is first front [line] end.
This is second front [line] end.
This is third front [line] end.
This is four front [line] end.
複製代碼

圓括號匹配

圓括號括起來的正則表達式所匹配的字符串能夠當成變量來使用,其中,\1表示第一個匹配,\2表示第二個匹配,以此類推)

$sed 's/^This is \([A-Za-z]*\) line.$/\1/g' 1.txt
first
second
third
four
複製代碼

正則表達式博大精深,每次都是學到用時方恨少!


N命令

這條命令會將下一行的內容送入緩存區,兩行併成一行進行匹配: (例以下面的命令,兩行合成一行後,兩行之間的換行符\n被替換成空格,因此輸出結果,原偶數行與奇數行合併)

$ sed 'N;s/\n/ /g' self.txt
First line     My name is John
Second line     I like milk
Third line     I like basketball
Four line     I live in china
複製代碼

a命令和i命令

a:表示append,在某行後面進行追加:

$ sed '$ a --- the end ---' 1.txt
This is first line.
This is second line.
This is third line.
This is four line.
--- the end ---
複製代碼

i:表示insert,在某行以前進行插入:

$ sed '1 i --- the start ---' 1.txt
--- the start ---
This is first line.
This is second line.
This is third line.
This is four line.
複製代碼

能夠進行匹配,而後在匹配行後面進行追加或者以前插入數據:

$ sed '/line/a I find a line' 1.txt
This is first line.
I find a line
This is second line.
I find a line
This is third line.
I find a line
This is four line.
I find a line
複製代碼

c命令

C命令:替換命令,將匹配到的行替換掉

單行替換:

$ sed '2 c You have been replaced' 1.txt
This is first line.
You have been replaced
This is third line.
This is four line.
複製代碼

多行替換

$ sed '2,$ c You have been replaced' 1.txt
This is first line.
You have been replaced
複製代碼

或者匹配行進行替換:

$ sed '/This is/c You have been replaced' 1.txt
You have been replaced
You have been replaced
You have been replaced
You have been replaced
複製代碼

d命令

d命令:delete,刪除 跟上面的替換c命令很相似

單行刪除:

$ sed '2 d' 1.txt
This is first line.
This is third line.
This is four line.
複製代碼

多行替換

$ sed '2,$ d' 1.txt
This is first line.
複製代碼

或者匹配行進行替換:

$ sed '/This is/d' 1.txt
# 都木有了=-=
複製代碼

p命令

p命令:print,打印命令,與grep相似

# 匹配到third這一行,可是重複輸出了
$ sed '/third/p' 1.txt
This is first line.
This is second line.
This is third line.
This is third line.
This is four line.
# 使用n參數進行過濾
$ sed -n '/third/p' 1.txt
This is third line.
# 多個模式匹配(兩個模式中間的行次也會被匹配出來)
$ sed -n '/first/, /third/p' 1.txt
This is first line.
This is second line.
This is third line.
# 指定某一行到匹配行進行打印
$ sed -n '2,/four/p' 1.txt
This is second line.
This is third line.
This is four line.
複製代碼

命令打包

多個命令可使用分號進行分開,使用大括號括起來做爲嵌套命令:

# 從一二行中,匹配到This字符,而後整行進行刪除
$ sed '1,2 {/This/d}' 1.txt
This is third line.
This is four line.
# 從一二行中,匹配到This字符,而後再匹配first字符,最後整行進行刪除
$ sed '1,2 {/This/{/first/d}}' 1.txt
This is second line.
This is third line.
This is four line.
# 多命令用分號;分開
# 例如刪掉包含first的行次,而且給每一行的開頭加上#號
$ sed '1,$ {/first/d;s/^/#/g}' 1.txt
#This is second line.
#This is third line.
#This is four line.
複製代碼

總結

這兩個文檔工具如grep同樣好用,並且感受awk的功能更增強大,建議你們去看耗子叔的酷殼博客,乾貨滿滿~


參考資料

耗子大神的網站值得一看~

  1. AWK 簡明教程
  2. SED 簡明教程
  3. 正則表達式工具網站
  4. 正則表達式學習文檔
相關文章
相關標籤/搜索