## 簡介
本文主要介紹 Linux 系統的兩個神級工具:sed 和 awk ,他們是Linux高手們必備的技能,很值得我們去研究的東西。
這里是我在網上書上收集的相關資料,因為這兩個工具很有名也很重要,所以這些資料會幫助我更好的了解和熟悉它們。
## 什么是sed
在《sed and awk》一書中(1.2 A Stream Editor)的解釋是:
Sed本質上是一個編輯器,但是它是非交互式的,這點與VIM不同;同時它又是面向字符流的,輸入的字符流經過Sed的處理后輸出。這兩個特性使得Sed成為命令行下面非常有用的一個處理工具。
Sed本身是一個管道命令,可以分析 standard input 的,主要是用來分析關鍵字的使用、統計等,此外還可以將數據進行替換、刪除、心中、選取特定行等功能。
## 基本概念
sed命令的語法如下所示:
~~~
sed?[-nefr]?[動作]
~~~
參數說明:
-n : 使用安靜模式,一般所有來自STDIN的數據會被列出到屏幕上,但是 -n 在可以只列出經過 sed 處理過的那一行。
-e : 直接在命令行模式上進行 sed 的動作編輯。
-f : 直接將 sed 的動作卸載一個文件內, -f filename 則可以執行 filename 內的 sed 動作。
-r : sed 的動作支持的是擴展型正則表達式的語法(默認是基礎正則表達式語法)。
-i : 直接修改讀取的文件內容,而不是由屏幕輸出。
動作說明: [n1],[n2] function
n1,n2:不見得會存在,一般代表選擇進行動作的行數。舉例來說:如果我的動作是需要在 10 到 20 行之間進行的,則“10,20[動作行為]”
function 有下面這些參數:
a:新增,a 的后面可以接字符串,而這些字符串會在新的一行出現(目前的下一行)。
c:替換,c 的后面可以接字符串,這些字符串可以替換n1,n2之間的行!
d:刪除,因為是刪除,所以 d 后面通常不接任何參數。
i:插入,i 的后面可以接字符串,而這些字符串會在新的一行出現(目前的上一行)。
p:打印,也就是將某個選擇的數據打印出來,通常 p 會與參數 sed -n 一起運行。
s:替換,可以直接進行替換工作。通常這個 s 的動作可以匹配正則表達式!例如:1,20s/old/new/g 就是。
## 關于 sed 的一些常見使用
以行為單位的新增或刪除功能
案例(一)
~~~
nl?/etc/passwd | sed?'2,5d'
~~~
說明:
* sed 的動作為 '2,5d',那個 d 就是刪除,命令運行的效果就是把2~5行給刪除。
* 需要注意的是,原本應該是要執行 sed -e 才對,沒有 -e 也行。
* 另外還需要注意的一點:sed 后面接的動作,請務必要以 '' 兩個單引號括住。
* 如果你只想刪除第二行,那么命令就是:nl /etc/passwd | sed '2d'。
* 如果你想刪除第三行到最后一行,那么就可以這么寫:nl /etc/passwd | sed '3,$d'
注:$表示最后一行。
案例(二)
~~~
nl?/etc/passwd | sed?'2a drink tea'
~~~
說明:
* 命令執行的效果就是在第二行后面(也就是第三行)加上“drink tea”字樣。
* 如果你想在第二行前面加上字符串,那么你可以這樣:nl /etc/passwd | sed '2i drink tea'
注:2a 中的 a 是指第二行后面,而 2i 中的 i 則是指第二行的前面。
案例(三)
~~~
nl /etc/passwd | sed '2a drink tea or ......\
drink beer'
~~~
說明:
* 上面的命令的執行效果是在第二行后面加入2行字。
* 在每一行的后面必須要以反斜杠 來進行新行的增加。
以行為單位的替換與顯示功能
案例(一)
~~~
nl?/etc/passwd | sed?'2, 5c No 2-5 number'
~~~
說明:
* 上面的命令的執行效果是將第2~5行的內容替換成“No 2-5 number”
案例(二)
~~~
nl?/etc/passwd | sed -n?'5,7p'
~~~
說明:
* 上面的命令執行的效果是僅列出文件中的第5~7行的內容。
* 命令中的 -n 代表的是安靜模式!但是這個參數建議加上。
案例(三)
~~~
sed?'s/要被替換的內容/新的內容/g'
~~~
說明:
* 以上命令執行的效果就是替換掉指定內容。
案例(四)
下面是記錄一次獲取IP數據的過程:
第一步:先查看源信息,利用 /sbin/ifconfig 查詢 IP。
~~~
/sbin/ifconfig eth0
~~~
注:我們的目的是要獲得IP數據,那么先利用關鍵字找出那一行。
第二步:利用關鍵字配合 grep 選取出關鍵的一行數據。
~~~
/sbin/ifconfig eth0?|?grep?'inet addr'
~~~
注:因為只需要IP數據,所以接下來就是把不需要的內容都刪掉,那么就需要一個正則表達式來幫助實現:
~~~
^.*inet addr:
~~~
第三步:將 IP 前面的部分予以刪除
~~~
/sbin/ifconfig eth0 | grep 'inet addr' | \
sed 's/^.*inet addr://g'
~~~
注:上面的命令就把 IP 前面的數據刪掉了,那么接下來就是把 IP 后面的數據也刪掉,此時的正則表達式則是:
~~~
Bcast.*$
~~~
第四步:將 IP 后面的部分予以刪除
~~~
/sbin/ifconfig eth0 | grep 'inet addr' | \
sed 's/^.*inet addr://g' | sed 's/Bcast.*$//g'
~~~
這樣就能把 IP 截取出來了~~~
案例(五)
這里主要是展示 sed 與正則表達式的配合使用。
假設我想在一個文件(你自己新建或者已有的,主要是測試而已)獲取MAN字樣的那幾行數據,但是#在內的批注我不需要,而且空白行也不要。
第一步:先使用 grep 將關鍵字 MAN 所在行取出來。
~~~
cat?/home/man.config | grep?'MAN'
~~~
第二步:刪除掉批注之后的數據。
~~~
cat?/home/man.config | grep?'MAN'?| sed?'s/#.*$//g'
~~~
第三步:那么接下來就是把空白行刪除掉。
~~~
cat /home/man.config | grep 'MAN' | sed 's/#.*$//g' | \
sed '^$/d'
~~~
直接修改文件內容(慎重)
首先要特別提醒的是,要練習 sed 修改文件內容的時候不能用任何系統配置文件,最好是自己新建一個測試文本來測試練習。
案例(一)
~~~
sed?-i?'/s\.$/\!/g'?test.txt
~~~
說明:
* 上面命令執行效果是利用 sed 將test.txt內的每一行結尾為“.” 的換成 !
* 命令中的 -i 參數可以讓你的 sed 直接去修改后面接的文件內容,而不是由屏幕輸出。
案例(二)
~~~
sed?-i?'$a?# This is a test'?test.txt
~~~
說明:
* 上面命令執行的效果是利用 sed 直接在test.txt最后一行加入 “This is a test”。
* 由于 $ 代表的是最后一行,而 a 的操作是新增,因此該文件最后新增。
## 什么是 awk
簡單來說,awk 是一個數據處理工具。
相比于 sed 常常作用于一整行的處理,awk 則比較傾向于將一行分成數個“字段”來處理。因此,awk 相當適合處理小型數據的數據處理。
對于編程語言來講,awk 是一種便于使用且表達能力強的程序設計語言,可應用于各種計算和數據處理任務。
## 基本概念
基本語法
~~~
awk?'條件類型1{動作1} 條件類型2{動作2} ...'?filename
~~~
* awk 后面接兩個單引號病加上大括號{}來設置想要對數據進行的處理動作。
* awk 可以處理后續接的文件,也可以讀取來自簽個命令的 standardoutput。
* 如前面說的,awk 主要是處理每一行的字段內的數據,而默認的字段的分隔符為空格鍵或者[tab]鍵。 比如:
~~~
last?-n?5?// 僅取出登陸者的數據前五行(last?可以將登陸者的數據取出來)
~~~
如果我還要在這些信息中取出:賬號與登陸者的IP,且賬號與IP之間以[tab]隔開,那么可以這么改命令:
~~~
last?-n?5?| awk?'{print?$1?"\t"?$3}'
~~~
上面是 awk 最常使用的動作,通過 print 的功能來講字段的數據列出來,字段的分割則以空格鍵或者[tab]按鍵來隔開。
上面的例子中,在每一行的每個字段都是有變量名稱的,那就是$1,$2等變量名稱。
備注:$1 指的就是第一列,但是 $0 則是代表一整行(第一行)。
上面的例子中整個awk的處理流程:
~~~
(1)讀入第一行,并將第一行的數據填入$0,$1,$2等變量中;
(2)依據條件類型的限制,判斷是否需要進行后面的動作;
(3)昨晚所有的動作與條件類型;
(4)若還有后續的“行”的數據,則重復上面1~3的不知,直到所有的數據都讀完為止。
~~~
* 注:awk是以行為一次處理的單位,而以字段最小的處理單位。
對于上面的案例指令 last -n 5 ... 來講,有幾點需要注意的:
* 列出每一行的賬號(就是$1)。
* 列出目前處理的行數(那就是 awk 內的 NR 變量)。
* 并且說明,該行有多少個字段(就是 awk 內的 NF 變量)。
注意: awk 后續的所有動作都是以單引號 “'” 括住的,由于單引號與雙引號都必須是成對的,所以 awk 的格式內容如查想要以 print 打印時,記得非變量的文字部分,包含一小節 printf 提到的格式中,都需要使用雙引號來定義出來,因為單引號已是 awk 的命令固定用法了。
~~~
last?-n 5 | awk '{print?$1?"\t lines: "?NR?"\t lines: "?NR?"\t columes: "?NF}'
~~~
## awk的一些常見使用
awk 的運算符
* 大于: >
* 小于: <
* 大于或等于: >=
* 小于或等于: <=
* 等于: ==
* 不等于: !=
值得關注的是那個等于 “==” 的符號,因為:
* 邏輯運算上面也就是所謂的大于,小于,等于等判斷式上面,習慣上是以“==”來表示的。
* 如果是直接給與一個值,例如變量設置時,就直接使用 = 而已。
案例(一)
~~~
cat /etc/passwd | \
awk '{FS=":"} $3 < 10 {print $1 "\t " $3}'
~~~
說明:上面的指令運行的效果是查閱第三列小于10以下的數據,并且僅列出賬號與第三列。(在 /etc/passwd 當中是以冒號“:”來作為字段的風,該文件中第一字段為賬號,第三字段則是UID)
案例(二)
~~~
cat /etc/passwd | \
awk 'BEGIN {FS":"} $3 < 10 {print $1 "\t " $3}'
~~~
說明:由于案例一中并沒有顯示出第一行的數據,那么此命令則是把第一行也顯示出來了。利用 BEGIN 關鍵字預先設置 awk 的變量。
案例(三)
此外 awk 還可以進行“計算功能”。
比如有一個文件,內容如下:
name 1st 2nd 3th
小A 1000 1000 1000
小B 2000 2000 2000
小C 3000 3000 3000
那么怎么計算每個人的總額呢?而且還需要格式化輸出,那么可以這樣思考:
* 第一行只是說明,所以第一行不要進行加總(NR==1時處理)。
* 第二行以后就會有加總的情況出現(NR>=2以后處理)。
~~~
cat test.txt | \
awk 'NR==1{printf "%10s %10s %10s %10s %10s\n",$1,$2,$3,$4, "Total" }
NR>2{total = $2 + $3 + $4
printf "%10s %10d %10d %10.2f\n", $1, $2, $3, $4, total}'
~~~
說明:
* 所有 awk 的動作(即在{}內的動作),如果有需要多個命令來輔助是,可利用分號“;”間隔,或者直接以[Enter]按鍵來隔開每個命令,上面則是摁了三次。
* 邏輯運算中,如果是“等于”的情況,則務必使用兩個等號“==”!
* 格式化處輸出時,在 printf 的格式設置當中,務必加上 n ,才能實現分行。
* 與 bash 、shell 的變量不同,在 awk 當中,變量可以直接使用,不需要加上 $ 符號。