Shell必備三劍客

梭梭666發表於2023-11-23

Top

學習要有計劃、程式碼要經常練習、學習之後要有輸出、多交流、多總結、找出規律。 忌三天打魚兩天曬網、淺嘗輒止、閉門造車

Sed——三劍客之一

基本格式

[root@localhost ~]# sed [選項] ['指令碼命令'] 檔名

選項及含義

選項 含義
-n 【none】 預設自動輸出處理後結果,該選項遮蔽預設輸出,可使用print命令來輸出匹配行。
-e 該選項會將其後跟的指令碼命令新增到已有的命令中。相互之間為同時滿足的關係
-i 此選項會直接修改原始檔,要慎用。
-f 指令碼命令檔案 該選項會將其後檔案中的指令碼命令新增到已有的命令中。
-r 支援擴充套件表示式

命令flags標記及功能

flags標記 含義
d 【delete】 刪除
s【space】 字串替換
c 行內容替換
g【global】 全域性匹配操作,否則只匹配操作【替換】第一次
r【read】 從指定檔案讀取內容
w【write】 將匹配結果儲存指定檔案
a【append】 指定行新增一行
i【insert】 指定行新增一行
q 指令碼退出命令
{} 命令組, 以 分號 分隔
= 顯示檔案行號
l 顯示控制字元

支援正規表示式, 擴充套件正規表示式

^ 錨點行首的符合條件的內容,用法格式"^pattern"
$ 錨點行首的符合條件的內容,用法格式"pattern$"
! 翻轉
& 之前匹配的內容
^$ 空白行
. 匹配任意單個字元
* 匹配緊挨在前面的字元任意次(0,1,多次)
.* 匹配任意長度的任意字元
\? 匹配緊挨在前面的字元0次或1次
\{m,n\} 匹配其前面的字元至少m次,至多n次
\{m,\} 匹配其前面的字元至少m次
\{m\} 精確匹配前面的m次{0,n}:0到n次
\< 錨點詞首----相當於 \b,用法格式:<pattern
\> 錨點詞尾,用法格式:>pattern
\<pattern\> 單詞錨點
\( . \)\1* 分組,用法格式:pattern,引用\1,\2
[] 匹配指定範圍內的任意單個字元
[^] 匹配指定範圍外的任意單個字元
\w 匹配字母或數字或下劃線
\s 匹配任意的空白符
\d 匹配數字,等價於[0-9]
\D 匹配非數字字元
\b 匹配單詞的開始或結束
[:digit:] 所有數字, 相當於0-9, [0-9]---> [[:digit:]]
[:lower:] 所有的小寫字母
[:upper:] 所有的大寫字母
[:alpha:] 所有的字母
[:alnum:] 相當於0-9a-zA-Z
[:space:] 空白字元
[:punct:] 所有標點符號
[:blank:] 空白字元(空格和製表符)
[:graph:] 可列印的非空白字元
[:print:] 可列印字元
[:cntrl:] 不可列印的控制字元(退格、刪除、警鈴...)
[:punct:] 標點符號

高階命令

# n 命令
提前讀取下一行,並且將之前讀入的行(在模式空間中的行)輸出到螢幕,然後後續的命令會應用到新讀入的行上
# N 命令
追加下一行到模式空間,同時將兩行看做一行,但是兩行之間依然含有\n換行符
# p 命令 
列印當前模式空間所有內容,追加到預設輸出之後
# P 命令
列印當前模式空間開端至第一個\n的內容,並追加到預設輸出之前 (輸出首行追加到預設輸出之前)
# d 命令
刪除當前模式空間內容(不再傳至標準輸出), 並放棄之後的命令,並對新讀取的內容,重頭執行sed
# D 命令
刪除當前模式空間開端至\n的內容(不在傳至標準輸出), 放棄之後的命令,但是對剩餘模式空間重新執行sed。
# y 命令
對之前匹配的字元逐個替換
# h 命令
將當前模式空間中內容覆蓋至快取區,
# H 命令
將當前模式空間中的內容追加至快取區
# g 命令
將當前快取區中內容覆蓋至模式空間,
# G 命令 
將當前快取區中的內容追加至模式空間
# x 命令
將當前快取區和模式空間內容互換, 下一行
"世界上只有一種真正的英雄主義,就是認清了生活的真相後,還依然執著地熱愛它。" ——羅曼·羅蘭

命令格式

- 該命令格式: [address]s/pattern/replacement/flags
	其中,address 表示指定要操作的具體行,pattern 指的是需要替換的內容,replacement 指的是要替換的新內容。
	關於指定具體操作行(address)的用法,這裡先不做解釋,文章後續會對其做詳細介紹。

注意

	sed 命令預設情況下逐行掃描檔案每一行,尋找匹配行,而若要特定的某一行或者一部分行,則必須寫明 命令格式 中的 address 部分, 意為定址方式。預設有兩種:
	- 以數字形式指定行區間: 2s/^#//g,  2,6s/^#//g,  1,$s/^#//g, ‘’
	- 用文字模式指定行區間:(/pattern/command):  /pattern/s/^#//g , /pattern01/,/pattern02/!p
	- 以數字和文字混合模式指定行區間:2,/pattern/p;  /pattern/,10!p

命令示例

字串替換----'s'

[root@localhost ~]# sed 's/test/trial/2' data4.txt   // 指定替換每行第二次
[root@localhost ~]# sed 's/test/trial/2w res.txt' data4.txt   //  替換每行第二次,並將匹配結果儲存到				res.txt
# 需注意跳脫字元的替換
[root@localhost ~]# sed 's/app/&end/g' emperor       // 將全域性的'app' 替換為 'append', & 代表前面匹配到的         字串
[root@localhost ~]# sed 's#/#\\\\#g' emperor       // 將全域性的 '/' 替換為 '\\' 
[root@localhost ~]# sed 's/^#//g emperor           // 將全域性的每行 開頭 '#' 替換為空  
[root@localhost ~]# sed 's#^//##g emperor           // 將全域性的每行 開頭 '//' 替換為空  
# 應用外部變數
[root@localhost ~]# name="Shi Wei Proxy!"          # 設定變數, 注意等號兩邊不可有空格
[root@localhost ~]# sed "s/xxx/$name/" emperor     # 使用變數替換, 注意:不可為單引號

行內容替換——'c'

[root@localhost ~]# sed '3c\
				> tihuan line ' emperor       // 將第三行內容替換為指定內容 

刪除——'d'

[root@localhost ~]# sed 'd' emperor        //  清空此檔案
[root@localhost ~]# sed '3d' emperor       //  刪除第3行
[root@localhost ~]# sed '1,3d' emperor       //  刪除第1-3行
[root@localhost ~]# sed '3,$d' emperor       //  刪除第3-最後的所有行

新增行——'i' 和 'a'

[root@localhost ~]# sed '3i\
			     >  insert line' emperor   // 在第三行 前 新增一行
[root@localhost ~]# sed '3a\
			     >  insert line' emperor   // 在第三行 後 新增一行

列印——'p'

[root@localhost ~]# sed -n '3p' emperor        //  列印第三行
[root@localhost ~]# sed '3q' emperor         //  列印檔案前三行,然後退出
[root@localhost ~]# sed -n '1p;3p' emperor        //  列印第 1 行和第 3 行
[root@localhost ~]# sed -n '3,$p' emperor        //  列印第3-最後行的所有內容

寫檔案——'w'

[root@localhost ~]# sed '3w ss' emperor        //  將第三行儲存到指定檔案
[root@localhost ~]# sed '3,$w ss' emperor        //  將第3-最後行儲存到指定檔案

讀檔案——'r'

[root@localhost ~]# sed '3r ss' emperor        //  在第三行後新增指定檔案內容
[root@localhost ~]# sed '$r ss' emperor        //  在檔案最後新增指定檔案內容

退出命令——'q'

[root@localhost ~]# sed '3q' emperor         //  列印檔案前三行,然後退出
[root@localhost ~]# sed '$q' emperor         //  列印檔案所有內容,然後退出

新增空白行

sed '$G' shiwei  # 在檔案 末尾新增 一空白行
sed '1G' shiwei  # 在第一行後加空白行
sed '/pattern/{G;}' filename

https://blog.csdn.net/whatday/article/details/105069960

高階玩法

# 文字倒序輸出
sed '1!G;h;$!d' shiwei 
# 輸出偶數行
sed 'n;p' -n shiwei
# 輸出奇數行
sed 'N;P' -n  或  sed –n '$!N;P' shiwei   或 sed 'n;d' a.txt 
# 替換
sed 'y/his/HIS/'

# xml 檔案
cat  core-site.xml  | sed 's/<name>\(.*\)<\/name>/\1/gp' -n
# 列印總行數
sed -n '$=' a.txt 
# 刪除多餘空格
sed 's/[ ]*//g' a.txt 
# 刪除倒數第二行
sed 'N;$!P;D' a.txt   
# 刪除最後兩行
sed 'N;$!P;$!D;$d' 
# VI 刪除包含strings前4行,後34行
:/strings/-4,+34d
# sed刪除匹配行的上一行
sed -i -n '$!N;/匹配內容/!P;D' 待處理文

# 刪除匹配行的上一行和下一行
sed -i -e '/string/{n;d}' -e '$!N;/\n.*string/!P;D' file
# sed中使用變數,刪除匹配行的上一行和下一行
AA=string     # 變數指定匹配字串
sed -i -e '/'"$AA"'$/{n;d}' -e '$!N;/\n.*'"$AA"'$/!P;D' file
# 獲取匹配行的 上一行
mst status | sed -n '/0000:61:00.0/{x;p};h'  # 


# 刪除指定行的上一行
sed  -e :a -e '$!N;s/.*\n\(.*0000:61:00.0.*\)/\1/;ta' -e 'P;D'
# 刪除指定字串之間的內容
sed -i '/ServerName abc.com/,/\/VirtualHost/d' $file
# 獲取匹配行的上一行
mst status | sed  '/0000:61:00.0/,$d' | sed '$p' -n
# 獲取匹配行的下一行 
mst status | sed  '1,/0000:61:00.0/d' | sed '1p' -n



# 每兩行 合併為一行
xargs -l2 < hebing | more    或者 cat hebing | xargs -l2 

# 解析 OS 映象儲存庫配置 XML 檔案 
sed -e  '/<id>\|^<name\|^<description/p' -e '/zh_CN/p' -n  <檔名>  | sed -e 's#<id>#id:   #g' -e 's#</id>##g' -e 's#<name xml:lang="zh_CN">#name:   #g' -e 's#</name>##g' -e 's#</description>##g' -e 's#<description xml:lang="zh_CN">#description:   #g'  | awk '{printf "%-18s",$1; i=2; while(i<=NF){printf $i; i++;}; printf "\n";}' 

參考網址

Sed 命令初級完全攻略

Sed 高階玩法

Ses 命令詳解

awk——三劍客之一

基本格式

[root@localhost ~]# awk [選項] '指令碼命令' 檔名
其中指令碼命令由 2 部分組成,如下所示: 
	'匹配規則{執行命令}'
注意: 整個指令碼命令是用單引號('')括起,而其中的執行命令部分需要用大括號({})括起來。其中的匹配規則類似於 sed       命令中的 address 部分, 可以文字指定行,也可以正則匹配。
在 awk 程式執行時,如果沒有指定執行命令,則預設會把匹配的行輸出;如果不指定匹配規則,則預設匹配文字中所有的行。
# 注意
	1、在awk中,如果省略了模式對應的動作,當前行滿足模式時,預設動作為列印整行,即{print $0}。
		awk '/root/' /shiwei/passwd
	2、在awk中,0或者空字串表示”假”,非0值或者非空字串表示”真”
	3. 當模式為真時,同時省略了對應動作時,預設動作為列印整行

選項及含義

選項 含義
-F fs 指定以 fs 作為輸入行的分隔符,awk 命令預設分隔符為空格或製表符。
-f 指定從一個指令碼檔案中讀取指令碼命令,取代命令列的方式。
-v var=val 在執行過程之前,定義變數,且初始值賦值為 val

awk 資料欄位變數

變數 含義
$0 代表整個文字行
$1 代表文字行中的第 1 個資料欄位;
$2 代表文字行中的第 2 個資料欄位
$n 代表文字行中的第 n 個資料欄位。

指令碼執行命令及含義

命令 或 關鍵字(內建變數) 含義
print 列印
BEGIN 在處理資料前執行一些指令碼命令
END 在讀完資料後執行一些指令碼命令
FS【Field Split】 輸入欄位分隔符, 預設為空格
RS【Record Split】 輸入記錄分隔符 ,**預設為 換行符 '\n' **
OFS 【Output Field Split 】 輸出欄位分割符, 預設為空格
ORS 【Output Record Split】 輸出記錄分隔符,預設為 換行符 '\n'.
FIELDWIDTHS 由空格分隔的一列數字,定義了每個資料欄位的確切寬度。將忽略 FS 變數
FNR 當前輸入檔案的所在行數,每個檔案每一行的該變數是固定的且從1開始。
NR 當前已處理的輸入記錄數。不同檔案行數累加。
delete 刪除陣列元素,格式: delete array[index]
NF 欄位數量, $NF表示最後一個欄位
FPAT 取得匹配的字元部分作為欄位, 與 FS 相反
RT
OFMT print 格式化輸出, 格式化定義變數

內建函式

exp(x)	# x 的指數函式。
int(x)	# x 的整數部分,取靠近零一側的值。
log(x)	# x 的自然對數。
rand()	# 比 0 大比 1 小的隨機浮點值。
srand(x)	# 為計算隨機數指定一個種子值。
# 數學運算
sqrt(x)	     # x 的平方根。
and(v1, v2)	# 執行值 v1 和 v2 的按位與運算。
or(v1, v2)	# 執行值 v1 和 v2 的按位或運算。
xor(v1, v2)	# 執行值 v1 和 v2 的按位異或運算。
compl(val)	# 執行 val 的補運算。
lshift(val, count)	# 將值 val 左移 count 位。
rshift(val, count)	# 將值 val 右移 count 位。
atan2(x, y)	   # x/y 的反正切,x 和 y 以弧度為單位。
cos(x)	# x 的餘弦,x 以弧度為單位。
sin(x)	# x 的正弦,x 以弧度為單位。
# 字串
index(s, t)	# 返回字串 t 在字串 s 中的索引值,如果沒找到的話返回 0。
length([s])	# 返回字串 s 的長度;如果沒有指定的話,返回 $0 的長度。
split(s, a [,r]) # 將 s 用 FS 字元或正規表示式 r(如果指定了的話)分開放到陣列 a 中,並返回欄位的總數。下標從1開始, 返回值就是分割以後的陣列長度
tolower(s)	# 將 s 中的所有字元轉換成小寫
toupper(s)	# 將 s 中的所有字元轉換成大寫。

# 正則匹配
match(s, r [,a])# 返回字串 s 中正規表示式 r 出現位置的索引。如果指定了陣列 a,它會儲存 s 中匹配正規表示式的那部分。
sub(r, s [,t]) 	# 在變數 $0 或目標字串 t 中查詢正規表示式 r 的匹配。如果找到了,就用字串 s 替換掉第一處匹配。
gsub(r, s [,t])	# 查詢變數 $0 或目標字串 t(如果提供了的話)來匹配正規表示式 r。如果找到了,就全部替換成字串 s。
substr(s, i [,n])	# 返回 s 中從索引值 i 開始的 n 個字元組成的子字串。如果未提供 n,則返回 s 剩下的部分。
gensub(r, s, h [, t]) 	# 查詢變數 $0 或目標字串 t(如果提供了的話)來匹配正規表示式 r。如果 h 是一個以 g 或 G 開頭的字串,就用 s 替換掉匹配的文字。如果 h 是一個數字,它表示要替換掉第 h 處 r 匹配的地方。


# 陣列元素排序
asort(s [,d])	# 將陣列 s 按資料元素值排序。索引值會被替換成表示新的排序順序的連續數字。另外,如果指定了 d,則排序後的陣列會儲存在陣列 d 中。返回值就是陣列的長度
asorti(s [,d])	# 將陣列 s 按索引值排序。生成的陣列會將索引值作為陣列元素值,用連續數字索引來表明排序順序。另外如果指定了 d,排序後的陣列會儲存在陣列 d 中。


# 時間戳
systime()	         # 返回當前時間的時間戳。
mktime(datespec)	 # 將一個按 YYYY MM DD HH MM SS [DST] 格式指定的日期轉換成時間戳值。
strftime(format [,timestamp])	# 將當前時間的時間戳或 timestamp(如果提供了的話)轉化格式化日期(採用 shell 函式 date() 的格式)。
# 列印
print    # 自帶換行符
printf   # 直接列印格式化值, 不自帶換行符
sprintf  # 返回格式化值

內建變數

FS:      欄位分隔符,預設是空格和製表符。等價於命令列 -F選項
RS:      行分隔符,用於分割每一行,預設是換行符。
OFS:     輸出欄位的分隔符,用於列印時分隔欄位,預設為空格。
ORS:     輸出記錄的分隔符,用於列印時分隔記錄,預設為換行符。
ARGC      命令列引數個數
ARGV      命令列引數排列
ENVIRON   支援佇列中系統環境變數的使用
FNR       瀏覽檔案的記錄數, 不過多檔案記錄不遞增,每個檔案都從1開始	
NR:      表示當前處理的是第幾行, 多檔案記錄遞增
NF:      表示當前行有多少個欄位
FILENAME:當前檔名

匹配模式

# <1> 空模式
	awk '{動作}' File 
	awk '{print $0}' File 
# <2> BEGIN/END 模式
	awk 'BEGIN{動作}' File  
	awk 'BEGIN{動作}END{動作}' File 
	awk '{動作; exit; }END{動作}' File 
# <3> 關係運算模式
	awk '關係表示式{動作}' File  
	awk 'NR > 3 && NR < 10 {print $0}' File 
	awk 'NF == 5 {print $0}' File 
	awk '$1 == 123 {print $0}' File 
# <4 >正則模式
	awk '/正規表示式/' File 
	awk '/^dhcp/{print $0}' /etc/passwd 
	awk '/\/bin\/bash$/{print $0}' /etc/passwd 
	# 注意: 
    	# 1、當在awk命令中使用正則模式時,使用到的正則用法屬於”擴充套件正規表示式”
		# 2、當使用 {x,y} 這種次數匹配的正規表示式時,需要配合 --posix 選項或者 --re-interval 選項。
		awk --posix '/.*.172.16.16.[0-9]{3} .*/{print $0}' dhcpd.leases  | sort -k 2 -r
		awk --posix '/.*.172.16.16.[0-9]{1,3} */{print $0}' dhcpd.leases  | sort -k 2
		awk --re-interval '/.*.172.16.16.[0-9]{1,3} */{print $0}' dhcpd.leases  | sort -k 2
# <5> 行範圍模式
	awk '/正規表示式/{動作}' File 
	awk '/正則1/,/正則2/{動作}' File 
	

關係運算子

關係運算子 含義 用法示例
< 小於 x < y
<= 小於等於 x <= y
== 等於 x == y
!= 不等於 x != y
>= 大於等於 x >= y
> 大於 x > y
~ 與對應的正則匹配則為真 x ~ /正則/
!~ 與對應的正則不匹配則為真 x !~ /正則/

總結如下

1. awk '/要匹配的內容/{進行的操作}' file,//內可以放入正規表示式,注意對一些特殊字元進行轉義,比如 "[]\"
2. awk 不能直接修改原始檔,可以透過重導向輸出結果來修改原檔案。>(全部覆蓋檔案內容)或>>(追加到檔案)
3. awk 使用 -v 定義變數,但是awk中引用變數時直接使用變數名,不需要在變數前加$
4. 多個字元作為分隔符(比如例子中的冒號和空格),可以使用|來區分;或者直接使用正則來作區分。比如例子中的-F":| +"可以寫成-F"[ 
5. 函式必須寫在BEGIN{}{}END{}的花括號之外的地方,不能放在任何{}內,否則會報錯`return' used outside function context
6. 格式化輸出用 prnitf 
7. awk 正則匹配無 字元邊界 '\b' 語法,  sed  有 

三元運算子

# 語法
條件?"結果1":"結果2"
awk -F: '{res=$3<500?"系統使用者":"普通使用者"; printf "%-20s  %-12s\n",$1,res;}' /etc/passwd
awk -F: '{$3<500?a++:b++;} END{printf "系統使用者數: %s\n普通使用者數: %s\n",a,b}' /etc/passwd


命令示例

awk 'BEGIN{FS=","; OFS="–"} {print $1,$2,$3}' data1

在 awk 中建立shell 變數

eval $(echo mysql-6.9.jar |awk -F"-" '{printf("name=%s;version=%s\n",$1,$2)}')

統計tcp網路連線數

netstat -an |awk  '/^tcp/{a[$NF]++}END{for(i in a){printf("%s:%d\n",i,a[i])}}'

匹配部分記錄


自定義變數

[root@suosuo ~]# awk 'BEGIN{
print "shiwei is a Good Boy!"
name = "wanganshi"
age = 200
print name
print "age = ", age
}'
shiwei is a Good Boy!
wanganshi
age =  200
$注意: 要新增 BEGIN 關鍵字, 在單引號中應用自定義的變數時,無需新增 $ 符號 
awk -v  age="shiwei" '{print age}'  a.txt  # -v選項宣告的變數在BEGIN{}、END{}和main程式碼段中都能直接使用
awk '{print age}' age="shiwei" a.txt       # 非選項型引數變數設定, 不能在 BEGIN 程式碼段中使用

列印奇偶行

# 列印奇數行 
awk 'i=!i' File 
# 列印偶數行 
awk '!(i=!i)' File 

高階玩法

# 返回每行 倒數 第二個 欄位, 小括號去除優先順序 
awk '{print $(NF-1)}' xxxx 
# 從ifconfig命令的結果中篩選出除了lo網路卡外的所有IPv4地址。
ifconfig | awk 'BEGIN{RS=""}!/lo:/{print $0}' | awk  '/inet /{print $2}'
# 同時處理多個檔案
awk '{print $1}' FS=' ' a.txt FS=":" /etc/passwd
# 忽略大小寫匹配
    # 轉換為小寫
    awk 'tolower($0) ~ /bob/{print $0}' a.txt
    # 設定IGNORECASE	
    awk '/BOB/{print $0}' IGNORECASE=1 a.txt
# 輸出除第3行外的所有行
awk 'NR==3{next}{print}' a.txt
awk 'NR==3{getline}{print}' a.txt
# 每個檔案只輸出前2行
awk 'FNR==3{nextfile}{print}' a.txt a.txt
# 匹配到某行之後,再讀一行就退出
awk '/^1/{print;getline;print;exit}' a.txt
# 每匹配到某行,並列印下一行
awk '/^1/{print;if((getline)<=0){exit};print}' a.txt
# 解析 .ini 配置檔案, 獲取配置塊
awk '/\[base\]/{print; while( (getline)>0 ){if(/\[.*\]/){exit}; if(!/^$/){print;} }}' mysql.ini
# 根據每一行中的某一欄位去重
awk -F ":" '! arr[$NF]++{print}' /etc/passwd
# 以行為單位, 進行次數統計
awk '{arr[$1]++}END{OFS="\t";for(idx in arr){print arr[idx],idx}}' cishu
# 過濾 某一欄位 
lsscsi | awk '($NF~/dev/){print $NF}'

# 過濾 CPU 資訊
cat /proc/cpuinfo | awk -v ii=0 '{if ($0 ~ /processor|vendor_id|model name|core id|cpu cores/) {if (ii<4){printf $0"\t"}else{printf $0"\t\n"; ii=-1;}; ii=ii+1;}}'
# 過濾有效記憶體插槽資訊
dmidecode -t memory | awk 'BEGIN{RS=""; FS="\n"}{if ($5 ~ /Total Width: Unknown/){}else{printf $0"}\n\n"; }}'
# 過濾網路裝置器詳細資訊
lshw -C network | awk 'BEGIN{RS="*-network[:0-9]*"; FS="\n"}{if ($0 ~ "virbr0"){}else {printf $0"\n";}}'
lshw -C network | awk -v name="0000:18:00.0"  'BEGIN{RS="*-network[:0-9 ]*[a-zA-Z]*\n";}{if ($0 ~ name) printf $0"\n"}
# 列印第2 個及之後的所有欄位 
awk '{i = 2; while (i<=NF) {print $i; i++}}'

# 解析網路卡硬中斷
cat /proc/interrupts | grep ens2f1- | awk '{printf("%s  %s\n",$1,$NF);i=2; of=NF-3; while(i<=of){if($i != 0){printf("%-10s%s---%s\n"," ",i-2,$i)}; i++;}}'

cat /proc/interrupts | grep ens2f1- | awk '{printf("\033[31m%s  %s\033[0m\n",$1,$NF);i=2; of=NF-3; while(i<=of){if($i != 0){printf("\033[34m%-10s%-4s--- %s\033[0m\n"," ",i-2,$i)}; i++;}}'
# 解析 dhcp 日誌檔案, 統計各個IP 
cat dhcpd.leases | grep 'lease' | awk 'BEGIN{printf "%-18s  %-7s\n","IP","Counts"}{count[$2]++}END{for(ff in count){printf "%-18s  %-7s\n",ff,count[ff]}}'

# 多次分隔——先將每行根據空格進行分割,然後根據‘,’分割
awk -F '[ ,]' '{print $1,$2,$5}' log.txt

# mellanox 網路卡, 根據中斷號,查詢其由哪些CPU在處理 
cat /proc/interrupts   | grep ens2np0  | sed '/299:/p' -n  | awk '{i=2; while(i<(NF-2)){if($i != 0){ print "iiii="(i-1)}; i++; }; }'


對 Fio 執行結果的有效資料提取

陣列的定義與遍歷

# 其實,awk中的陣列本來就是”關聯陣列”,之所以先用以數字作為下標的陣列舉例,是為了讓讀者能夠更好的過度,不過,以數字作為陣列下標的陣列在某些場景中有一定的優勢,但是它本質上也是關聯陣列,awk預設會把”數字”下標轉換為”字串”,所以,本質上它還是一個使用字串作為下標的關聯陣列。
# awk中的陣列本質上是關聯陣列,所以預設列印出的元素是無序的。
# awk中陣列的下標預設是從 1 開始的 , 但也可以從  0 開始 
# 在awk中,當變數a的值為字串時,也可以進行加法運算,但如果字串參與運算,字串將被當做數字0進行運算, 空字串在參與運算時,也會被當做數字0
# 我們對一個不存在的元素進行自加運算後,這個元素的值就變成了自加運算的次數


[root@suosuo shiwei]# awk 'BEGIN{
ss["name"] = "shiwei"
ss["age"] = 200
ss["salary"] = "23000RMB"
for(dex in ss)
{
print "dex:",dex,"value:",ss[dex]
}
}'
$注意: 下標引數不可為 "index"
# 獲取最後一列
awk '{print $NF}'

# 定義陣列,並檢視結果
awk 'BEGIN{a[1]="hello";a[2]="word";a["name"]="meitian";for(i in a){print "key為"i":value為"a[i]}}'
# 遍歷陣列
awk 'BEGIN{id[0]=0; id[1]="11"; id[2]="222"; id[3]="3333"; for(ff in id){print id[ff];}}'
awk 'BEGIN{id[0]=0; id[1]="11"; id[2]="222"; id[3]="3333"; for(ff=0; ff<=3; ff++){print id[ff];} }'
# 遍歷陣列索引
awk 'BEGIN{id[0]=0; id[1]="11"; id[2]="222"; id[3]="3333"; for(ff in id){print ff;} }'
# 判斷陣列元素是否定義, 
# 注意: 陣列各個元素值預設為空,即使為顯示定義
[root@R2700G3_13 demo_awk]# awk 'BEGIN{id[0]=0; id[1]="11"; id[2]="222"; id[3]="3333"; if(4 in id){print "exists"}else {print "not exists"}}'
not exists
[root@R2700G3_13 demo_awk]# awk 'BEGIN{id[0]=0; id[1]="11"; id[2]="222"; id[3]="3333"; id[4]=""; if(4 in id){print "exists"}else {print "not exists"}}'
exists
# 取反 !  
awk 'BEGIN{id[0]=0; id[1]="11"; id[2]="222"; id[3]="3333"; id[4]=""; if( ! (4 in id)){print "not exists"}else {print "exists"}}'
# 陣列的下標不僅可以為”數字”,還可以為"任意字串"
awk 'BEGIN{ff[0]=100; ff["name"]="shiwei"; ff["age"]=300; ff["salary"]="3000k"; printf "ID: %s \nNAME: %s \nAGE: %s \nSALARY: %s \n",ff[0],ff["name"],ff["age"],ff["salary"]}'

# 刪除陣列元素
awk 'BEGIN{ff[0]=100; ff["name"]="shiwei"; ff["age"]=300; delete ff[0]; exit } END{for(aa in ff){printf "%-6s:  %-8s\n",aa,ff["age"]}}'




分支結構 if ... else ....

[root@suosuo shiwei]# cat ww
10
23
56
100
43
600
[root@suosuo shiwei]# awk '{
if($1 % 2 == 0)print $1 * 2;
else print $1 / 2}' ww
20
11.5
112
200
21.5
1200
# 判斷是否為數字, 運用正則
echo 4234 | awk '{if ($0 ~ /^[0-9]+$/) print "1"; else print "0";}'

ip link|grep -v -E "virbr|lo"|awk -F: '{if($1 ~ /^[0-9]+/) print $2}'|sort |sed 's/^[\t]*//g'

lsblk | awk '/disk/{printf("%s:  %d\n",$1, $4)}END{if($4 >= 5)print $0 }'
# 支援浮點數運算
lsblk | awk '/disk/{if($4 >= 1.9) printf("%s:  %.2f\n",$1, $4) }'
# 取反
awk '{while ( ! ($0 < 10) ){printf "%s  %s\n",NR,$0; break };}' watermelon

迴圈結構

1. 可以使用 while、do-while、for 這 3 種迴圈結構
2. awk 還支援使用 break(跳出迴圈)、continue(終止當前迴圈)關鍵字
while (條件) {
   執行程式碼; 
}

for (var in array)
{
    statements
}

for(變數;條件;計數器)
{
    執行程式碼;
}

do
{
執行程式碼;
}while(條件)
# 在 引用 自定義的 變數時 不要加 '$' 符號, 直接用
echo | awk '{ss=100; for(i=1;i<=ss;i++){printf("%.2f\n",i)} }'
echo | awk '{ss=100;i=1; while(i<=ss){ printf("%.2f\n",i); i=i+1; } }'
echo | awk '{ss=100;i=1; do{ printf("%.2f\n",i); i=i+1; }while(i<=ss) }'

陣列

awk 使用關聯陣列提供陣列功能
# 刪除陣列元素
delete array[index]

自定義函式

注意: 1. 自定義函式的函式名必須能夠唯一標識此函式,換句話說,在同一個 awk 指令碼程式中,多個函式的函式名不能相同。同時,函式的引數可以有多個(0 個、1 個或多個)。
2. 函式還能用 return 語句返回值
3. 在定義函式時,它必須出現在所有程式碼塊之前(包括 BEGIN 和 END程式碼塊)
function 函式名(引數1,引數2,…)
{
    執行程式碼;
}

4. awk 提供了一種途徑來將多個函式放到一個庫檔案中,這樣使用者就能在所有的 awk 指令碼程式中使用了。為了方便大家理解,下面給大家舉個例項。
首先,我們需要建立一個儲存所有 awk 函式的檔案:
[root@localhost ~]# cat funclib
function myprint() {
   printf "%-16s – %sn", $1, $4
}
function myrand(limit)
{
   return int(limit * rand())
}
function printthird()
{
   print $3
}

要想讓 awk 成功讀取 funclib 函式庫檔案,就需要使用 -f 選項,但此選項無法和 awk 指令碼程式同時放到命令列中一起使用。因此,要使用庫函式檔案,只能再建立一個指令碼程式檔案,例如:

[root@localhost ~]# cat script4
BEGIN{ FS="n"; RS=""}
{
     myprint()
}
[root@localhost ~]# awk -f funclib -f script4 data2
Riley Mullen     – (312)555-1234
Frank Williams   – (317)555-9876
Haley Snell      – (313)555-4938

單獨程式碼塊的定義

# 使用 大括號 包圍程式碼 
# 注意以下 2 點: 
	1.  大括號內部前後必須保留一個空格 
	2. 每個命令都要以分號 (;) 作為顯式結尾; 

其他

ethtool --show-priv-flags ens16f0 | sed '2,$p' -n | awk 'BEGIN{FS=":"}{print $1}'
for name in `ethtool --show-priv-flags ens16f0 | sed '2,$p' -n | awk 'BEGIN{FS=":"}{print $1}'`; do ethtool --set-priv-flags ens16f0  $name on; done


參考網址

awk 命令詳解

grep——過濾工具——三劍客之一

選項及含義

選項 含義
-v 顯示不匹配的行
-E 擴充套件正規表示式egrep
-F fgrep 查詢速度比grep命令快,但是不夠靈活:它只能找固定的文字,而不是規則表示式
-i 不區分大小寫
-q【quiet】 靜默模式,不輸出任何資訊,在shell指令碼中,可以透過echo $? 檢視是否匹配到,0表示匹配到,1表示沒有匹配到
-m【--max-count=NUM】 匹配的最大數
-n 【--line-number】 顯示的加上匹配所在文字的行號
-w 【--word-regexp】 匹配整個單詞
-e 實現多個引數之間的 ‘或’ 的關係
-l 【--files-with-matches】 匹配多個檔案時,顯示匹配的檔名
-L 匹配多個檔案時,顯示不匹配的檔名
-c 顯示匹配的行數
-n 【--no-filename】 當搜尋多個檔案時,不顯示匹配檔名字首
-o 【--only-matching】 只顯示每一行中匹配 到 的部分, 不列印匹配的完整行內容
-b 【--byte-offset】 顯示塊【字元或者位元組】偏移
-r 【--recursive】 在多級目錄中對文字進行遞迴搜尋
-A 【--after-context=NUM】 列印匹配本身以及之後的 num 行
-B 【--before-context=NUM】 列印匹配本身以及之前的 num 行
-C 【--context=NUM】 列印匹配本身以及之前之後的 num 行
-f【--file=FILE】 從檔案中讀取正規表示式

基本正規表示式元字元:

egrep = grep -E
# 字元匹配	
.:         # 匹配任意單個字元;
[]:        # 匹配指定範圍內的任意單個字元
[^]         # 表示取反:匹配指定範圍外的任意單個字元
[:digit:]   # 單個數字
[:lower:]   # 單個小寫字元
[:upper:]   # 單個大寫字元
[:alpha:]   # 單個字母,不區分大小寫
[:alnum:]   # 單個字母或者數字
[:punct:]   # 單個標點符號
[:space:]  # 單個空白字元
[^  ]       # 表示匹配指定範圍外的任意單個字元
# 匹配次數:用在要指定次數的字元後面,用於指定前面的字元要出現的次數,預設工作在貪婪模式:儘可能長的匹配
* :   匹配前面的字元任意次,包括0次
.* :  任意長度的任意字元
\? :  匹配其前面的字元0或1次
\+ :  匹配其前面的字元至少1次,但不需要緊隨在第一次後面
\{m\} :匹配前面的字元m次
\{m,n\} :匹配前面的字元至少m次,至多n次
\{,n\} :匹配前面的字元至多n次
<=n次
\{m,\} :匹配前面的字元至少m次
>=m次
#  位置錨定:定位出現的位置
^    :  行首錨定,用於模式的最左側
$    行尾錨定,用於模式的最右側
^PATTERN$: 用於模式匹配整行
^$: 空行
^[[:space:]]*$ :空白行或包含tab字元的行
# 預設情況下,grep就無法識別”\d”這種簡短格式,可以使用-P選項,表示grep使用相容perl的正規表示式引擎
\d 表示任意單個0到9的數字
\D 表示任意單個非數字字元
\t 表示匹配單個橫向製表符(相當於一個tab鍵)
\s 表示匹配單個空白字元,包括"空格","tab製表符"等
\S 表示匹配單個非空白字元
\< 或 \b :詞首錨定,用於單詞模式的左側
\> 或 \b :詞尾錨定;用於單詞模式的右側
注意:在grep中,字母和數字的組合也被視為單詞
\<PATTERN\>:錨定以PATTERN表示式做為單詞的行
# 分組及引用
\(\):將1個或多個字元捆綁在一起作為一個整體進行處理
\(xy\)*ab:xy作為一個整體出現0此或多次
   注意:分組括號中的模式匹配到的內容會被正規表示式引擎記錄與內部變數中,這些變數為:
    \1:模式從左側起,第一個左括號以及與其匹配的右括號之間的內容
    \2:模式從左側起,第二個左括號以及與其匹配的右括號之間的內容
    \3:
    ……
    後向引用:引用前面的分組括號中的模式所匹配的字元,且引用中的資料和分組模式匹配到的資料是一樣
[^0-9]與[^[:digit:]]等效
[^a-z]與[^[:lower:]]等效
[^A-Z]與[^[:upper:]]等效
[^a-zA-Z]與[^[:alpha:]]等效
[^a-zA-Z0-9]與[^[:alnum:]]等效
# 去除 dmesg 日誌中前部時間------- 5 種方法
dmesg -T  | sed 's/^\[.*\]\(.*\)/\1/gp'
dmesg -T  | awk '{ii=6;while(ii<=NF){printf("%s ",$ii); ii++};print " "}' 
dmesg -T  | cut -d']' -f2
dmesg -T  | egrep '](.*)' -o | cut -d']' -f2
dmesg -T  | grep -P -o '(?<=]).*' 

貪婪匹配

預設情況下匹配都是貪婪模式,如果要改成非貪婪模式,只需要量詞後面加上一個問號?
貪婪模式常用的量詞有:{m,n} 、{m,}、 ? 、*、 +
Linux上grep執行不生效,和bsd和GUN協議有關,bsd用grep -oE    GUN用grep -oP
cat rhel.ipxe | xargs | grep -oP ':RHEL8.4x86.*?boot'

命令示例

過濾出檔案的空白行和註釋行: 兩次過濾

cat /etc/mysql/mariadb.conf.d/50-server.cnf | grep -v '^#' | grep -v '^$'
# 等同於 
grep -Eiv '^#|^$' /etc/mysql/mariadb.conf.d/50-server.cnf

快速過濾 出某一檔案中的 包含特定字元的 的 行

grep  ‘server’  /etc/mysql/mariadb.conf.d/50-server.cnf

同時過濾對個檔案,並同時顯示在不同檔案所匹配行號與檔名

root@server46 ~/D/f/r/tmp [2]# grep -nw panda *   # 代表匹配當前目錄下的所有檔案
one:2:panda
three:4:panda
two:1:panda

同時指定多個過濾字串

grep -nwr -E 'panda|eagle'  ./ 
#  等價與 
grep -nwr -e panda -e eagle  ./ 

指定要過濾的,要排除的檔案

# 指定要過濾的檔案
grep -nwr -e panda --include four *
grep -nwr -e panda --include 'tw*' *
grep -r "main" ./ --include *.{php,html}
# 指定要排除的檔案
grep -nwr -e eagle --exlcude three *
# 指定 排除 某一檔案中的檔案 
grep -nwr panda --exclude-from ex_file  *

簡單正則匹配

# 獲取mellanox 網路卡 介面卡型別
lspci -vvv –s  <Bus 號>  | grep -i ethernet | egrep -o  '(\[.*\])'  # 結果: [ConnectX-4 Lx]

獲取正規表示式分組資料

# 獲取mellanox 網路卡 介面卡型別
lspci -vvv -s  0000:01:00.0  | grep -i ethernet | sed -r "s#.*\[(.*)\]\$#\1#g" # 結果: ConnectX-4 Lx
# 判斷網路卡是電口還是光口
ethtool <網口名> | grep 'Supported ports:' | sed -r  's#.*\[(.*)\]$#\1#g' | sed 's/ //g'
# 公共資料
data='jdbc.url=jdbc:mysql://www.xxxx.com:3308/db_xxx?abcdefgh'
host=`echo $data | grep "jdbc:mysql" | sed -r "s/.*:mysql:\/\/(.*):(.*)\/(.*)\?.*/\1/g"`
port=`echo $data | grep "jdbc:mysql" | sed -r "s/.*:mysql:\/\/(.*):(.*)\/(.*)\?.*/\2/g"`
dbname=`echo $data | grep "jdbc:mysql" | sed -r "s/.*:mysql:\/\/(.*):(.*)\/(.*)\?.*/\3/g"`
	# 結果如下:
$host="www.xxxx.com"
$port="3308"
$dbname="db_xxx"

查詢不同檔案互異之處

# 查詢兩個檔案的互異之處 , 包含順序
diff sw46 sw48
# 查詢兩個檔案的相同行 , 不包含順序
cat sw48 | grep -f sw46 -o
# 查詢第一個檔案中與第二個檔案中不存在的行 , 不包含順序
grep -v  "`cat sw48 | grep -f sw46 -o`" sw48

Bash 與 Sh(Dash) 的區別

# RedHat 下 sh 預設指向 bash  
# Ubuntu 下 sh 指向 dash 
1. dash  無法識別 echo 命令的 '-e' 引數


參考 URL

grep和egrep正規表示式

Linux之grep命令詳解---001

linux中grep命令詳解---002

相關文章