Linux中的awk、sed、grep及正則表達式詳解
簡介
awk、sed和grep是Linux中文本操作的三大利器。
其中awk適用于取列,sed適用于取行,grep適用于過濾。
正則表達式
首先我們來介紹一下正則表達式,正則表達式(regular expression)描述了一種字符串匹配的模式(pattern),可以用來檢查一個串是否含有某種子串、將匹配的子串替換或者從某個串中取出符合某個條件的子串等。
正則表達式是文本操作的好幫手。
以下是正則表達式的元字符及含義說明。
元字符 | 功能 |
---|---|
^ | 匹配行首 |
$ | 匹配行尾 |
^$ | 空行 |
. | 匹配任意單個字符,包括0次 |
* | 匹配多個字符,包括0次 |
[ ] | 匹配指定范圍內的任意單個字符 |
[^] | 匹配不在指定范圍內的任意字符 |
.* | 匹配任意長度任意字符,不包括0次 |
.. | 匹配子串 |
& | 保存搜索字符用來替換其他字符 |
\< | 匹配單詞的開始,其后任意字符必須作為單詞的首部出現 |
\> | 匹配單詞的結束,其前任意字符必須作為單詞的尾部出現 |
x\{m\} | 匹配字符x出現的次數,m次 |
x\{m,\} | 匹配字符x出現的次數,至少m次 |
x\{m,n\} | 匹配字符x出現的次數,至少m次,不多于n次 |
? | 匹配前面的字符1次或0次 |
有了正則表達式的幫助,我們就可以正式開始來看文本操作三兄弟了。
awk
awk 是對文本進行格式化的工具,適合處理比較復雜的格式處理。有多個版本: 1、new awk: nawk ;2、gawk, awk
awk 命令格式
awk [options] 'script' file1 file2, ...awk [options] 'PATTERN {acticon}' file1 file2, ...
格式說明
-
pattern部分決定動作語句何時觸發及觸發事件:BEGIN、END
-
action 對數據進行處理,放在{}內指明:print、printf
-
最常用的是 print,默認以空白字符分隔
$0 代表整行,$1 代表第 1 段,$2 代表第 2 段,以此類推,$NF 代表最后一個字段,多個字段直接用逗號隔開
awk '{print $1, $2}' xxx.log
打印操作支持拼接打印,如:
awk '{print "first" $1, $2}' xxx.log
-
options 參數:
參數 | 含義 |
---|---|
-F | 輸入分隔符,默認以空白字符進行分隔 |
-v | 輸出分隔符,默認為空格,使用內置變量OFS來設置輸出分隔符 |
awk內置變量
變量名 | 含義 |
---|---|
FS | 輸入字段分隔符,默認空白字符,一般需要加-F |
OFS | 輸出字段分隔符,默認是空格,一般需要加-v |
NF | 分隔后的字段數量 |
NR | 當前行的行號 |
實例
有時候我們想要拿到當前目錄下的所有文件名,但是如果直接 ls -a | grep "^-"
(grep "^-"
是過濾只顯示文件,下面會詳細講),得到的結果是:
ls -l | grep "^-"
# 輸出
-rw-r--r-- 1 root root 487305 May 1 06:45 3d1.txt
-rw-r--r-- 1 root root 10004085 May 1 06:45 3d1_image_urls.txt
-rw-r--r-- 1 root root 167934 May 1 06:45 3d1_singleface.txt
-rw-r--r-- 1 root root 9516780 May 1 06:45 3d1_urls.txt
-rw-r--r-- 1 root root 1289925 May 1 06:45 adzz_3d1_urls.txt
如果我們只想要文件名本身,而不想要其他相關信息數據,該怎么辦呢?我們觀察到 ls -l
的輸出信息是很有規律的,并且就是以空白分隔的,這時我們就可以使用 awk
命令:ls -l | grep "^-" | awk '{print $9}'
從而達到我們想要的效果:
ls -l | grep "^-" | awk '{print $9}'
# 輸出
3d1.txt
3d1_image_urls.txt
3d1_singleface.txt
3d1_urls.txt
adzz_3d1_urls.txt
sed
sed 是一種流編輯器,它一次處理一行內容。處理時,把當前處理的行存儲在臨時緩沖區中,稱為“模式空間”,接著用 sed 命令處理緩沖區中的內容,處理完成后,把緩沖區的內容送往屏幕。
命令格式
sed [options]... 'script' inputfile
參數
參數 | 含義 |
---|---|
-n | 不輸出模式空間,即不自動打印 |
-e | 多點編輯 |
-f [PATH_TO_SCRIPT_FILE] | 從指定文件中讀取編輯腳本 |
-r | 支持使用擴展正則表達式 |
-i | 直接編輯文件 |
-i.bak | 備份文件并遠處編輯 |
script 地址定界
- 不給地址:對全文進行處理
- 單地址:
- # 指定的行
- $ 最后一行
- /pattern/ 被此處模式所能匹配到的每一行
編輯命令
命令 | 說明 |
---|---|
d | 刪除模式空間匹配的行,并立即啟動下一輪循環 |
p | 顯示符合條件的行,追加到默認輸出之后 |
a [\]text | 在指定行后面追加文本,支持使用\n實現多行追加 |
i [\]text | 在行前面插入文本 |
c [\]text | 替換行為單行或多行文本 |
w path/file | 保存模式匹配的行之指定文件 |
r path/file | 讀取指定的文件的文本至模式空間中匹配的行后 |
= | 為模式空間的行打印行號 |
! | 模式空間中匹配行去反處理 |
s/// | 查找替換,支持使用其他分隔符,s@@@,s### |
替換標記
- g 行內全局替換
- p 顯示替換成功的行
- w path/file 將替換成功的行保存至文件中
例:高效地查看環境變量
sed -i 's/a/v/g' test
將文件中的 a 全部替換為 v ,sed替換格式是:sed -i ‘s/要替換的內容/替換成的內容/g'
文件名
比如說,我們要查看環境變量:
echo $PATH
# 輸出
# /home/ps/anaconda3/condabin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
我們知道,這樣直接打印的話,輸出是按照冒號來進行分隔的,可讀性不佳,為了改善這一點,我們可以用sed
將冒號替換為換行符:
echo $PATH | sed "s/:/\n/g"
# 輸出:
# /home/ps/anaconda3/condabin
# /usr/local/sbin
# /usr/local/bin
# /usr/sbin
# /usr/bin
# /sbin
# /bin
# /usr/games
# /usr/local/games
# /snap/bin
這樣可讀性就會好很多。
grep
grep 強大的文本搜索工具,根據模式搜索文本,并將符合模式的文本行顯示出來。
命令格式:
grep [option] pattern [file]
option 參數
參數 | 含義 |
---|---|
-i | 忽略字符大小寫 |
-n | 顯示匹配的行號 |
-v | 顯示沒有被匹配的行,即反轉查找 |
--color | 將匹配到的字符以高亮顏色顯示出來 |
-c | 統計匹配的行數 |
-o | 僅顯示匹配到的字符串 |
-q | 靜默模式,不輸出任何信息 |
-w | 匹配整個單詞 |
-A | after,顯示后行 |
-B | before,顯示前行 |
-C | context,顯示前后行 |
-E | 相當于egrep,即grep -E == grep |
例
這里我們以很常用的查看目錄中文件/目錄的個數命令為例,分析此例中grep的用法:
這是筆者在網上搜到的查看目錄中的文件數:ls -l | grep "^-" | wc -l
的命令,我們來看一下他都干了什么。
首先要明確中間的兩道 |
是管道,不了解的讀者可以簡單地理解為將前一命令的輸入直接作為后一命令的輸出。
-
我們先
ls
當前目錄手動數一下有多少文件。藍色字體的都是目錄,白色字體則是一些文本文件。可以看到,當前目錄下有3個文件,5個目錄。
再用查到的命令來試一下
ls -l | grep "^-" | wc -l
沒有問題,與預期相符。
-
第一條命令
ls -l
,我們來看打印輸出了什么是各個文件/目錄的一些詳細信息。我們注意到每一行的第一個字符
d
或者-
分別代表了本行表示的是一個目錄,還是一個文件。相必整條命令應該也是借助這一點來判斷當前目錄下的子目錄/文件個數的。 -
我們再看第二條命令
grep "^-"
, 果然,我們之前提到過,正則表達式的^
就是來匹配第一個字符的,而要查的是文件的個數,自然應該找第一個字符為-
的行。那找目錄是不是就是應該匹配行首的d
呢?我們來試一下:果然,與我們數的子目錄個數也是符合的。這樣我們就知道了怎樣來查看當前目錄下子目錄個數。
另外,讀者可以自己試一試,既然一個目錄內不是子目錄就是文件,那能不能使用grep的反轉查找參數
-v
來統計子目錄數呢? -
第三條命令是
wc -l
,我們知道wc
命令是用來統計字符數的,而加上-l
命令則可以統計行數,可以支持我們grep過濾并通過行數來查看文件數的需求。但是,我們注意到,grep命令本來就自帶一個參數-c
來統計匹配成功的行數,那是不是可以直接得到文件數呢?我們來試一試ls -l | grep -c "^-"
可以看到,也是OK的。是一種更簡便的寫法。