第十一章 正則表達式與文件格式化處理
11.1 基礎概念
正則表達式是對字符串的處理的表示方式。
11.2 基礎正則表達式
11.2.1 語系
採用LANG=C
的語系。
特殊符號 | 意義 |
---|---|
[:alnum:] | 英文大小寫字符和數字 0-9 a-z A-Z |
[:alpha:] | 英文大小寫字符 a-z A-Z |
[:blank:] | 代表空格鍵和Tab鍵兩種空格符 |
[:cntrl:] | 代表 鍵盤上面的控制按鍵,包括CR LF Tab Del 等 |
[:digit:] | 數字 0-9 |
[:graph:] | 除了空格符(空格鍵、Tab鍵)之外的其他所有按鍵 |
[:lower:] | 小寫字母 a-z |
[:upper:] | 大寫字母 A-Z |
[:print:] | 任何可以被打印出來的字符 |
[:punct:] | 標點符號 |
[:space:] | 任何會產生空白的字符,包括空格鍵、Tab鍵、CR等 |
[:xdigit:] | 十六進制數字,包括0-9 A-F a-f |
grep [-A] [-B] [--color=auto] '查找字符' filename
-A 後可接數字,爲after的意思,除了列出該行之外,後續的n行也跟着列出
-B 後可接數字,爲before的意思,除了列出該行之外,前面的n行也跟着列出
dmesg | grep -n 'tun' 從dmesg輸出的所有內容中,將帶有關鍵字tun的那行帶行號顯示
dmesg | grep -n -A3 -B 2 'tun' 同上,在此基礎上,將前面的2行和後面的3行一起顯示出來
11.2.3 基礎正則表達式練習
調整語系:
export LANG=C
export LC_ALL=C
grep -n 'the' regular_express.txt 有the的行顯示
grep -vn 'the' regular_express.txt 無the的行顯示
grep -ni 'the' regular_express.txt 有不論大小寫的the的行都顯示
grep -n 't[ae]st' regular_express.txt 有test的和taste的行都會被顯示出來,[ae]表示a或者e
grep -n '[^a-z]oo' regular_express.txt 字符串'oo'前面未出現小寫字母的行都顯示
grep -n '[^[:lower:]]oo' regular_express.txt 同上
grep -n '[0-9]' regular_express.txt 有數字的行都顯示,
grep -n '[[:digit:]]' regular_express.txt 同上
grep -n '[0-9][0-9]*' regular_express.txt 同上,這是使用正則表達式匹配,上面使用通配符匹配
grep -n '^the' regular_express.txt 開頭是the的行都顯示
grep -n '^[a-z]' regular_express.txt 開頭是小寫字母的行都顯示
grep -n '^[[:lower:]]' regular_express.txt 同上
grep -n '^[^a-zA-Z]' regular_express.txt 開頭別是英文字母的行都顯示
grep -n '^[^[:alpha:]]' regular_express.txt 同上
grep -n '\.$' regular_express.txt 行尾是小數點的行都顯示
grep -n '^$' regular_express.txt 顯示空白行,結果是22:
grep -n '^$' regular_express.txt | cat -A 顯示空白行,並顯示特殊字符,結果是22:$
grep -v '^$' /etc/rsyslog.conf | grep -v '^#' 空白行與開頭是井號鍵的註釋行都不顯示
grep -n 'g..d' regular_express.txt 將g與d之間存在兩個任意字符的那行顯示
grep -n 'oo*' regular_express.txt 第一個o務必存在,後面的o*表示可以有0到無窮多個o
grep -n 'ooo*' regular_express.txt 含有兩個或更多連續o的行顯示
grep -n 'goo*g' regular_express.txt 字符串首尾都是g,且中間有1個以上的連續o,沒有其他雜字符
grep -n 'g.*g' regular_express.txt 字符串首尾都是g就行,不管中間有啥有多少,都顯示
grep -n 'o\{2\}' regular_express.txt 含有兩個o的行顯示
grep -n 'go\{2,5\}g' regular_express.txt 找到g後面接2-5個o,之後又有個g的行顯示
grep -n 'go\{2,\}g' regular_express.txt 找到g後面接2個或2個以上的o,只有又有個g的行顯示
^,在字符集合符號(中括號[])內外表示含義不同,在內表示反向選擇,在外表示定位在行首。
Windows下的換行符是^M$ ,而Linux下的換行符是$。兩者有別。
通配符*,代表任意字符,0個或多個;小數點.代表【一定有一個任意字符】;星號*代表【重複前一個字符,0到無窮多次】,是組合形態。
cat -An regular_express.txt |head -n 10 | tail -n 5 將第5-10行顯示出來,帶行號,且顯示特殊字符
輸出:
6 GNU is free air not free beer.$
7 Her hair is very beauty.$
8 I can't finish the test.$
9 Oh! The soup taste good.$
10 motorcycle is cheap than car.$
11.2.4 基礎正則表達式字符集合
RE字符 | 意義 |
---|---|
^word | 帶查找的字符串word在行首 |
word$ | 帶查找的字符串word在行尾 |
小數點. | 一定有一個任意字符 |
\ | 轉義符,將特殊符號的特殊意義去除 |
* | 重複0個到無窮多個的籤一個RE字符 |
[list] | 字符集合的RE字符,裏面列出想要選取的字符 |
[n1-n2] | 字符集合的RE字符,裏面列出想要選取的字符範圍 |
[^list] | 字符集合的RE字符,裏面列出不要的字符串或範圍 |
{n,m} | 連續n到m個的前一個RE字符;{n}是連續n個的前一個RE字符;{n,}連續n個以上的前一個RE字符 |
ls -l * 匹配任意文件
ls -l a* 匹配文件名開頭是a的文件
ls | grep -n '^a.*' 匹配文件名開頭是a的文件
ls -l /etc | grep '^l' 找到/etc下的鏈接文件【lrwxrwxrwx】開頭是l
ls -l /etc | grep '^l'|wc -l 這樣的文件總共有多少個,列出個數
11.2.5 sed工具
sed本身也是管道命令,可以分析標準輸入。可以完成替換、刪除、新增、選取特定行等功能。
sed [-nefr] [操作]
-n 安靜模式,只有經過sed處理的行才顯示到屏幕上
-e 直接在命令行模式上進行sed的操作編輯
-f 直接將sed的操作寫在一個文件內,-f filename 可以執行filename內的sed操作
-r sed的操作使用的是擴展性正則表達式語法(默認是基礎正則表達式語法)
-i 直接修改讀取的文件內容,而不是由屏幕輸出
操作:[n1[,n2]] function
n1,n2 未必存在,選擇進行操作的行數
function包括: a 新增
c 替換
d 刪除
i 插入
p 打印
s 替換
nl /etc/passwd | sed '2,5d' 刪除2-5行後的內容顯示在屏幕上
nl /etc/passwd | sed '2d' 刪除第2行後的內容顯示在屏幕上
nl /etc/passwd | sed '3,$d' 刪除第3行到最後一行之後的內容顯示在屏幕上
nl /etc/passwd | sed '/^$/d' 刪除空白行
nl /etc/passwd | sed '2a drink tea' 即在第二行結束,第三行開頭添加drink tea字樣
nl /etc/passwd | sed '2i drink tea' 即在第二行前面,第一行後面添加drink tea字樣
可以新增好幾行數據,必須用轉義字符+Enter鍵隔開:
nl /etc/passwd | sed '2a drink tea or \
> drink bear?'
替換整行:
nl /etc/passwd | sed '2,5c No 2-5 number' 將第2-5行數據替換成No 2-5 number
輸出:
1 root:x:0:0:root:/root:/bin/bash
No 2-5 number
6 sync:x:5:0:sync:/sbin:/bin/sync
按行數輸出整行內容:
nl /etc/passwd | sed -n '5,7p'
以行爲單位,實現部分數據的查找和替換:
sed 's/要被替換的字符/新的字符/g'
sed 's/要被替換的字符//g' 將匹配到的字符串刪除
cat /etc/man_db.conf | grep 'MAN'|sed 's/#.*$//g' 刪除掉每一行註釋之後的內容,原本的註釋行都變成了空白行
cat /etc/man_db.conf | grep 'MAN'|sed 's/#.*$//g' | sed '/^$/d' 因此還得刪除空白行
直接修改文件,將文件末尾的點換成感嘆號:
sed -i 's/\.$/\!/g' regular_express.txt
直接修改文件,在文件末尾添加一句註釋# This is a test
sed -i '$a # This is a test' regular_express.txt
11.3 擴展正則表達式
RE字符 | 意義 |
---|---|
+ | 重複1個或1個以上的前一個RE字符 |
? | 0個或1個的前一個RE字符 |
| | 用或(or)的方式找出數個字符串 |
() | 找出羣組字符串 |
()+ | 多個重複羣組的判別 |
11.4 文件的格式化與相關處理
11.4.1 格式化打印:printf
printf '打印格式' 實際內容
\a 警告聲音輸出
\b 退格鍵(backspace)
\f 清屏
\n 輸出新行
\r 回車按鍵
\t 水平的Tab鍵
\v 垂直的Tab鍵
\xNN NN是兩位數數字,可以轉換數字成爲字符
%ns 多少個字符
%ni 多少個整數位數
%N.nf 共要N個位數,其中小數點佔n位
printf '%s\t %s\t %s\t %s\t \n' $(cat printf.txt) 要先把文件中的內容提取出來給printf用
printf '\x45\n' 看十六進制的數值45代表的字符是啥
11.4.2 awk:好用的數據處理工具
awk比較傾向於一行當中分成數個字段來處理,適合處理小型文本數據。
awk '條件類型1{操作1} 條件類型2{操作2} ...' filename 默認分隔符是空格鍵和Tab鍵
last -n 5 | awk '{print $1 "\t" $3}' 顯示前5行中的第一字段和第三字段(awk最常用的功能)
last -n 5 | awk '{print $1 "\t lines:" NR "\t columns:" NF}' 輸出字段1,目前處理的第幾行NR,該行有幾個字段NF
cat /etc/passwd | awk '{FS=":"} $3 < 10 {print $1 "\t" $3}' 將第3字段的UID號碼小於10的那行的字段1和字段3顯示出來(這種方式,第一行數據無法正確處理,那個時候的分隔符還是默認爲空格)
cat /etc/passwd | awk 'BEGIN {FS=":"} $3 < 10 {print $1 "\t" $3}' 將第3字段的UID號碼小於10的那行的字段1和字段3顯示出來(解決上述問題)
變量名稱 | 意義 |
---|---|
NF | 每一行($0)擁有的字段總數 |
NR | 目前awk所處理的是第幾行數據 |
FS | 目前的分隔字符,默認是空格鍵 |
11.4.3 文本比對工具
diff通常用在同一個文件(或軟件)的新舊版本差異上,以行爲單位比對。不要用diff比較兩個不相關的文件。
diff [-bBi] from-file to-file
from-file 文件名,作爲原始比對文件的文件名
to-file 文件名,作爲目標比對文件的文件名
from-file和to-file可以用-來替換,-此處代表標準輸入
-b 忽略一行當中,僅有多個空白的差異,認爲“a a”與“a a”相同
-B 忽略空白行的差異
-i 忽略大小寫的不同
mkdir -p /tmp/testpw
cd /tmp/testpw/
cp /etc/passwd passwd.old
cat /etc/passwd | sed -e '4d' -e '6c no six line'>passwd.new
diff passwd.old passwd.new
輸出:
4d3 左邊第4行被刪除了,基準是右邊的第三行
< adm:x:3:4:adm:/var/adm:/sbin/nologin 列出了左邊文件被刪除的那一行內容
6c5 左邊文件的第6行被替換成右邊文件的第5行
< sync:x:5:0:sync:/sbin:/bin/sync 左邊文件第6行
---
> no six line 右邊文件第5行
diff /etc/rc0.d/ /etc/rc5.d/ 運行級別0和運行級別5的啓動腳本文件對比
輸出:
只在 /etc/rc0.d/ 存在:K90network
只在 /etc/rc5.d/ 存在:S10network
cmp [-l] file1 file2
-l 將所有不同點的字節處都列出來,默認僅輸出第一個發現的不同點
cmp passwd.old passwd.new
輸出:
passwd.old passwd.new 不同:第 106 字節,第 4 行
diff -Naur passwd.old passwd.new > passwd.patch 製作補丁文件
cat passwd.patch
輸出:
--- passwd.old 2020-06-07 16:02:19.455870231 +0800
+++ passwd.new 2020-06-07 16:03:01.811869325 +0800
@@ -1,9 +1,8 @@
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
-adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
-sync:x:5:0:sync:/sbin:/bin/sync
+no six line
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
需要安裝軟件才能使用:
patch -pN < patch_file
patch -R -pN < patch_file
patch -p0 < passwd.patch 用剛剛製作出來的patch文件更新舊文件
patch -R -p0 <passwd.patch 恢復舊文件
11.4.4 文件打印設置:pr
打印的時候,打印標題和頁碼的設置。
pr /etc/man_db.conf
11.6 習題
grep '\*' /etc/* 2>/dev/null 找到/etc文件夾下的帶*內容
grep '\*' $(find /etc -type f) 2>/dev/null 找到/etc包括子文件夾下的文件的帶*內容
grep -l '\*' $(find /etc -type f) 2>/dev/null 找到符合的文件名就行,不用找內容