常用文字處理命令
目錄
- 一、awk
- 基本句式
- 過濾記錄
- 指定分隔符
- 特殊關鍵字:
- 正則
- 輸出到不同的檔案
- 和環境變數的互動
- 二、grep
- 三、sed
- 四、sort和uniq
- 五、實戰
- 處理以下檔案內容,將域名取出並進行計數排序,如處理:
- awk例子
Linux中很多文字工具都使用到了正則表示式,正則表示式可以極大的簡化linux系統管理工作,因為網上有很多正則相關的教程,所以這裡不再講述,我當時看的是菜鳥的正則表示式,看個一下午在實驗幾遍基本就會了,除了正向肯定預查,反向肯定預查這幾個比較複雜一些,其他都是非常簡單的,很多時候記不住也可以查詢網上對著寫,並不需要你實時記住。這裡主要談談awk等用到正則表示式的文字處理工具。
一、awk
awk的指令必須包含在單引號中。
基本句式
awk -F'指定輸入分隔符' 'BEGIN{做一些初始化工作} 一些過濾條件 {針對每行的工作}... END{最後收尾工作}'
中間的處理塊可以有多個,通過過濾條件單每行都會走一遍過濾條件,其中BEGIN和END邊只會執行一遍
過濾記錄
- awk '$3==0 && $6=="LISTEN" ' netstat.txt
- awk '$3==0 && $6=="LISTEN" || NR==1 ' netstat.txt
指定分隔符
awk -F: '{print $1,$3,$6}' /etc/passwd
awk 'BEGIN{FS=":"} {print $1,$3,$6}' /etc/passwd
awk -F '[;:]'
指定多個分隔符awk -F: '{print $1,$3,$6}' OFS="\t" /etc/passwd
指定輸出分隔符
需要注意的是上面print $1,$3,$6
中的,
被替換成了分隔符,如果是print $1$3$6
則中間沒有分隔符
特殊關鍵字:
- NR 目前處理的行號
- NF 當前處理的行一共用到的欄位數目
- FNR 目前處理的檔案的行號(當處理多個檔案時,NR會不停的累加,但如果是FNR則在處理新檔案是從1開始)
- FILENAME 檔名
- $0 當前整行
- FS 輸入欄位分隔符 預設是空格或Tab
- RS 輸入記錄分隔符 預設為換行符
- OFS 輸出欄位分隔符 預設是空格或Tab
- ORS 輸出記錄分隔符 預設為換行符
正則
- 普通匹配:
awk'/hello/ {print}' test.sh
- 匹配取反:
awk '!/hello/ {print}' test.sh
- 同時匹配:
awk '/hello/ && /world/ {print}' test.sh
- 或者匹配:
awk '/hello/ || /world/ {print}' test.sh
也可以寫成awk '/hello|world/ {print}' test.sh
- 指定列匹配:
awk '$5 ~ /hello/ {print}' test.sh
- 指定列匹配取反:
awk '$5 !~ /hello/ {print}' test.sh
輸出到不同的檔案
$ awk 'NR!=1{if($6 ~ /TIME|ESTABLISHED/) print > "1.txt"; else if($6 ~ /LISTEN/) print > "2.txt"; else print > "3.txt" }' netstat.txt
awk 'NR!=1{print > $6}' netstat.txt
其實使用了 >
重定向,上例子使用了if語句
- 統計資料:
awk 'NR!=1{a[$6]++;} END {for (i in a) print i ", " a[i];}' netstat.txt
- 行數篩選,開頭和結尾的條件使用,分隔: awk '/test1/,/test2/ {print}' test.txt
和環境變數的互動
$ x=5
$ y=10
$ export y
$ echo $x $y
5 10
$ awk -v val=$x '{print $1, $2, $3, $4+val, $5+ENVIRON["y"]}' OFS="\t" score.txt
Marry 2143 78 89 87
Jack 2321 66 83 55
Tom 2122 48 82 81
Mike 2537 87 102 105
Bob 2415 40 62 72
二、grep
引數列表:
- -w 匹配整個單詞
- -s 忽略檔案不存在等報錯
- -l 僅列出匹配檔案列表
- -L 僅列出不匹配檔案列表
- -A 顯示後的行數 如-1 匹配行的後1行
- -B 顯示前的行數 如-1 匹配行的前1行
- -number 顯示前後的行數 如-1 匹配行的前後1行
- -n 列印行數
- -c 僅顯示個數
- -v 反向
- -o 僅顯示匹配的內容
- -E 則表示要使用EREs
- -P 則表示要使用PREs
grep主要就是一個正則表示式的使用,其中需要注意正則有三種BREs、EREs和PREs。前兩種不支援非貪婪匹配。grep預設是BREs,所以他?,+,|,{,},(,)
這種字元都需要用\
轉義,另外他不支援\s,\S,\D,\d,\n
等字元。
三、sed
sed命令在自動化指令碼編寫的過程還是經常用到的。
基本句式: sed -nfei [操作]
操作: n1,n2 動作
動作:
- d: 刪除
- s: 替換,行內替換,行內匹配的字串,如hello world該行替換hello為hi變成hi world
- a和i: a增加在匹配的後面增加行 i增加在匹配的前面增加行
- c :替換,針對整行替換
例子:
sed -e 's/hello/hi/g'
:替換文字,-e可以省略sed -e '1,2s/hello/hi/g' -e '3,4s/world/man/g
:等價於sed -e '1,2s/hello/hi/g;3,4s/world/man/g
sed s/hello \(world\)/\1 hi/g'
:群組匹配,可以使用\n選擇前面的群組
四、sort和uniq
sort引數
- -r: 預設升序,-r表示反序
- -u: 移除重複
- -o: 重定向到檔案,注意
sort test.txt >test.txt
不可用,因為> 是想清空檔案,所以會導致檔案在排序之前就清空了 - -n: 預設按字元排序,如10小於2,-n表示按數字排序
- -t: 指定分隔符
- -k: 指明用哪一列來做排序
- -b: 忽略每行前面開始出的空格字元
例子:
sort -t $'\t' -k 1 -u res.txt > res2.txt
以tab作為分隔符,按第一列排序並去重
uniq引數
需要注意uniq需要文字是有序的,所以一般使用uniq的時候是用更早sort的管道後面
- -c:顯示出現的次數
- -d:僅顯示重複出現行;
- -u :僅顯示出一次的行列;
說說sort|uniq
和sort -u
,一直覺得很奇怪,兩者有什麼區別,功能是一樣的。sort -u
是後面加入的,所以很多人還是使用了sort|uniq
,
目前推薦使用sort -u
,因為還少了程序間通訊。
五、實戰
處理以下檔案內容,將域名取出並進行計數排序,如處理:
http://www.baidu.com/index.html
http: / / www.baidu.com/1.html
http://post.baidu.com/index.html
http://mp3.baidu.com/index.html
http://www.baidu.com/3.html
http://post.baidu.com/2.html
得到如下結果:
3 www.baidu.com
2 post.baidu.com
1 mp3.baidu.com
解法1:grep -Po '(?<=//)(.*?)(?=/)' test.txt |sort |uniq -c|sort -nr
1.利用了Perl,他支援非貪婪,2.利用了正向和反向預查(正向預查是後面的(?=)) 3.利用了-o引數只輸出匹配的內容
解法2:awk -F/ '{print $3}' test.txt |sort |uniq -c|sort -nr
指明瞭分割符號 直接取對應值
解法3:sed 's/http:\/\/\([^/]*\).*/\1/' test.txt|sort |uniq -c|sort -nr
基本的正則中小括號需要轉義,如果採用-r引數即擴充套件的正則小括號不用轉義
解法4: sed -e 's/http:\/\///' -e 's/\/.*//' | sort | uniq -c | sort -rn
採用了替換,先替換前面的,在替換後面的
awk例子
需要注意awk不支援多維陣列,採用了一種變通的方式,普通的使用沒問題,但是如果需要存的值是一個map就不合適了,如下
檔案 1-6列分別為deal od sum up lj day ,現在要計算sum up lj day 的累加和輸出
輸出也要是deal od sum up lj day也就是sum up lj day需要是一個map,不過awk做不到這點
{
updealids:{
od: {day,sum,up,lj}
}
}
awk 'BEGIN{OFS="\t"}{result[$1,$3,"sum"]+=$4;result[$1,$3,"up"]+=$5;result[$1,$3,"lj"]+=$6;result[$1,$3,"day"]=$2}\
END{for ( i in result) {split(i, a, SUBSEP); print result[i] ,a[1], a[2], a[3] }}' *
參考資料:
- AWK 簡明教程
- SED 簡明教程