Linux文本處理三劍客

目錄

相關命令

正則表達式

基本正則表達式元字符:

字符匹配:

次數匹配:

位置錨定:

擴展的正則表達式元字符:

字符匹配:

次數匹配:

位置錨定:

一、grep

1.作用

2.用法及常用選項

練習1

3.egrep

練習2

4. fgrep

二、sed

1.常用選項

2.地址定界

3.編輯命令

練習

4.高級編輯命令

三、awk

1.print

2.變量

3.printf

4.操作符

5.PATTERN

6.常用的action

7.控制語句

8.array

9.函數

回顧

參考資料

相關命令

      grep:文本過濾(模式:pattern)工具:文本查找,尋找含有特定文本的行

                  grep, egrep, fgrep

      sed:stream editor,文本編輯工具(行)

      awk:Linux上的實現gawk,文本報告生成器,使文本更好看

正則表達式

      模式:由正則表達式字符及文本字符所編寫的過濾條件

正則表達式REGEXP:由一類特殊字符及文本字符所編寫的模式,其中有些字符不表示字符字面意義,而表示控制或通配的功能

            分兩類:

                       基本正則表達式:BRE         grep

                       擴展正則表達式:ERE         grep -E, egrep

正則表達式引擎:用來檢查表達式是否能夠匹配到那行文本信息

基本正則表達式元字符:

字符匹配:

            .  匹配任意單個字符

            [ ] 匹配指定範圍內的任意單個字符

            [^]匹配指定範圍外的任意單個字符  注意^必須得在至少一個[ ]內

            [:digit:]、[:lower:]、[:upper:]、[:alpha:]、[:alnum:]、[:punct:](所有標點符號)、[:space:]  

次數匹配:

用在要指定次數的字符後面,用於指定前面的字符要出現的次數

            *匹配前面的字符任意次;>=0

                       例如:grep "x*y"

                             abxy

                             xay             *:y前面的x出現0次,也符合匹配條件

                             xxxxxxy       默認工作在貪婪模式

                  與glob中的*有區別

            .*任意長度的任意字符,此時與glob中單個*意義一致

            \?匹配其前面的字符0或1次,即前面的可有可無

            \+匹配其前面的字符至少1次;>0

\{m\}匹配前面的字符m次,有轉義字符\纔不會因爲{ }而展開命令行

                  \{m,n\}匹配前面的字符至少m次,至多n次                                           \{0,n\}匹配前面的字符至多n次

                  \{m,\}匹配前面的字符至少m次

位置錨定:

            ^:行首錨定;用於模式的最左側;e.g. ^root

            $:行尾錨定;用於模式的最右側

                  ^PATTERN$:用於模式匹配整行

                  ^$:空行         ^[[:space:]]*$  假空行

            \<\b:詞首錨定:用於單詞模式的左側

            \>\b:詞尾錨定:用於單詞模式的右側

            \<PATTERN\>:匹配整個單詞

分組:

                  \( \):將一個或多個字符捆綁在一起,當作一個整體進行處理

                             \(xy\)*ab

                       如果不加()

Note分組括號中的模式匹配到的內容會被正則表達式引擎記錄於內部的變量中,這些變量的命名方式爲:\1, \2, \3, ...(\n爲在grep的命名方式,其他可能是$n)

\1:從左側起,第一個左括號以及與之匹配右括號之間的模式所匹配到的字符

                                   \(ab\+\(xy\)*\):

                                         \1:ab\+\(xy\)*

                                         \2:xy

後向引用:引用前面的分組括號中的模式所匹配的字符,(而非模式本身)

e.g.1 匹配以t結尾,且t前面有1-3個字母

e.g.2 匹配符合條件(t結尾,在t之前有1-3個字母開頭)的單詞

e.g.3 首尾t前{1,3}個字母不同

e.g.4 運用後向匹配,首尾t前{1,3}個字母相同

擴展的正則表達式元字符:

字符匹配:

            .  匹配任意單個字符

            [ ] 匹配指定範圍內的任意單個字符

[^]:匹配指定範圍外的任意單個字符

次數匹配:

            擴展的正則表達式次數匹配不用反斜線 \ 轉義

            *匹配前面的字符任意次;>=0

            ?0或1次

            +1次或多次

            {m}匹配m次

            {m,n}至少m,至多n次

位置錨定:

            ^:行首錨定

            $:行尾錨定

            \<, \b:詞首錨定

            \>, \b:詞尾錨定

分組:

                  ()

                  後向引用:\1, \2, ...

            或者:

                  a|b  a或者b

                       e.g. C|cat表示的是”C或cat”,而不是”Cat或cat”

                             (C|c)at表示是”Cat或cat”

一、grep

      grep:Global search REgular expression and Print out the line.

1.作用

文本搜索工具,根據用戶指定的“模式”對目標文本逐行進行匹配檢查,打印匹配到的行

2.用法及常用選項

grep [OPTIONS] PATTERN [FILE...]  模式兩邊要加引號

            選項:

                  --color=auto:對匹配到的文本着色顯示

                  -v:顯示不能夠被pattern匹配到的行

                  -i:忽略字符大小寫

                  -o:僅顯示匹配到的字符串

                  -q:靜默模式,不輸出任何信息,只需要判斷結果是否存在時有用

                  -A #:after, 後#行

                  -B #:before, 前#行

                  -C #:context, 前後各#行

                  -E:使用ERE;

                  -F:使用快速正則表達式

                  -P:使用prel正則表達式

練習1

顯示/proc/meminfo文件中以大小寫s開頭的行;(要求:使用兩種方式)

方法1:[root@Tux ~]# grep '\<[Ss].*' /proc/meminfo

      改進:grep ^[Ss]   /proc/meminfo

方法2:[root@Tux ~]# grep -i ^S /proc/meminfo

方法3:grep -e ^S -e ^s  /proc/meninfo

顯示/etc/passwd文件中不以/bin/bash結尾的行

[root@Tux ~]# grep -v '/bin/bash$' /etc/passwd

[root@Tux ~]# grep -v '(/bin/bash)\>' /etc/passwd   (此做法錯誤:\>用於單詞詞尾錨定)

顯示/etc/passwd文件中ID號最大的用戶的用戶名

分析:cut 取用戶名-> sort -k3n排序->如何取出最大一行 tail -1

[root@Tux ~]# sort -t:-k3n /etc/passwd | cut -d:-f1 | tail -1

nfsnobody

[root@Tux ~]# sort -t:-k3nr /etc/passwd | cut -d:-f1 | head -1

nfsnobody

如果用戶root存在,顯示其默認的shell程序

            # id root &> /dev/null && grep "^root\>" /etc/passwd | cut -d:-f7

            P.S. 也可以將root後的\>換成:

找出/etc/passwd中的兩位或三位數

錯誤做法:grep '[0-9]\{2,3\}' /etc/passwd 這樣提取出來的可能不是純數字

            # grep " \< [0-9]\{2,3\} \> " /etc/passwd

顯示/etc/rc.d/rc.sysinit文件中,至少以一個空白字符開頭的且後面存非空白字符的行

      錯誤做法:grep '^[[:space:]]\+.*[^[:space:]].*' /etc/rc.d/rc.sysinit

                       非空白字符沒有僅跟在空白字符後面,題目理解有偏差

            # grep "^[[:space:]]\+[^[:space:]]" /rc.d/rc.sysinit

找出"netstat -tan"命令的結果中以'LISTEN'後跟0個、1個或多個空白字符結尾的行

            # netstat -tan | grep "LISTEN[[:space:]]*$"

添加用戶bash、testbash、basher以及nologin(其shell爲/sbin/nologin);而後找出/etc/passwd文件中用戶名同shell名的行

錯誤做法1:grep '\<\([[:alpha:]].*\):.*\1\>' /etc/passwd 模式成了一個單詞

錯誤做法2:grep '^\([[:alpha:]]\+\).*\1$' /etc/passwd

      P.S. ↑[:alpha:]應改成 [:alnum:]

      useradd -s /sbin/nologin nologin

            # grep " ^\([[:alnum:]]\+ \> \) .* \1$ " /etc/passwd

3.egrep

      支持擴展的正則表達式,相當於grep -E,其他選項和grep一樣

      egrep = grep -E

      egrep [OPTIONS] PATTERN [FILE...]

練習2

顯示當前系統root、centos或user1用戶的默認shell和UID

      egrep '^root|centos|user1\>' /etc/passwd |cut -d: -f1,7,3 --output-delimiter='  '

            # grep -E '^(root|centos|user1)\>' /etc/passwd | cut -d:-f1,3,7

找出/etc/rc.d/init.d/functions文件(CentOS6)中某單詞後面跟一個小括號的行

            # grep -E -o "^[_[:alpha:]]+\(\)" /etc/rc.d/init.d/functions

使用echo輸出一絕對路徑,使用egrep取出其基名

      命令:basename    去掉前導的目錄部分後打印"名稱"

basename /usr/bin/sort       輸出"sort"

命令:dirname       輸出已經去除了尾部的"/"字符部分的名稱;如果名稱中不包含"/",則顯示"."(表示當前目錄)。

 dirname /usr/bin/sort  輸出"/usr/bin"

            dirname stdio.h           輸出"."

            # echo "/mnt/sdc" | grep -E -o "[^/]+/?$" | cut -d"/" -f1

進一步地:使用egrep取出路徑的目錄名,類似於dirname命令的結果

# echo "/etc/rc.d/init.d" | egrep -o '^/.*/' | egrep -o '^/.*([[:alpha:]]|[0-9])\>' || echo "."

找出ifconfig命令結果中1-255之間的數值

      # ifconfig | egrep '\<1?[[:digit:]]{,2}\>|\<25[0-5]\>|\<2[0-4][[:digit:]]\>'

找出ifconfig命令結果中的IP地址(滿足x.x.x.x即可)

# ifconfig | egrep -o '([[0-9]]{1,2}|1[0-9]{1,2}|25[0-5]|2[0-4][[0-9])\.([0-9]{1,2}|1[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-9]{1,2}|1[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-9]{1,2}|1[[0-9]{1,2}|25[0-5]|2[0-4][[0-9])'

4. fgrep

不支持正則表達式搜索,全按字符本身搜索

二、sed

sed:Stream EDitor, 行編輯器

用法:

      sed [option]... 'script' inputfile...

            script:

                  '地址命令'

1.常用選項

行爲模式:

      sed讀取每個文件,一次讀一行,將讀取的行放到內存的模式空間;編輯命令修改模式空間中的內容;(未編輯結束的內容可以暫時放到保持空間,而保持空間當中的內容又可以重新被讀取到模式空間,隨後可繼續編輯)當所有操作完成後,sed將模式空間的最後內容打印到標準輸出

選項:

-n:不輸出模式中的內容至屏幕;默認行爲是將模式空間的內容輸出到屏幕

-e:多點編輯   P.S. 多點編輯時,每個模式前都要加-e

-f /PATH/TO/SCRIPT_FILE:從指定文件中讀取編輯腳本

-r:支持使用擴展正則表達式

-i:原處編輯

2.地址定界

①不給地址:對全文進行處理

②單地址

     #:指定的行

     /pattern/:被此處模式所能夠匹配到的每一行

③地址範圍

     #,#

     #,+#

     /pat1/,/pat2/

     #,/pat1/

④ ~:步進

     1~2

     2~2

3.編輯命令

d:刪除

p:顯示模式空間中的內容

a \text:在行後面追加文本;支持使用\n實現多行追加

i \text:在行前面插入文本;支持使用\n實現多行插入

c \text:替換行爲單行或多行文本

w /path/to/somefile:保存模式空間匹配到的行至指定文件中 (另存爲)

r /path/from/somefile:讀取指定文件的文本流至模式空間中匹配到的行的行後

=:爲模式空間中的行打印行號

!:取反條件;

s///:查找替換;支持使用其它分隔符,s@@@,s###

     替換標記:

           g:行內全局替換

           p:顯示替換成功的行

           w /PATH/TO/SOMEFILE:將替換成功的結果保存至指定文件中

P.S. p命令和要地址定界一起使用,也就是打印被定界匹配到的行

練習

①刪除/boot/grub/grub.conf文件中所有以空白開頭的行行首的空白字符;

sed 's@^[[:space:]]\+@@' /boot/grub/grub.conf

②刪除/etc/fstab文件中所有以#開頭,後面至少跟一個空白字符的行的行首的#和空白字符

sed 's@^#[[:space:]]\+@@' /etc/fstab

③echo一個絕對路徑給sed命令,取出其基名;取出其目錄名

      取出其基名

echo /etc/rc.d/init.d/testsrv | sed "s@/.*/@@"

      取出其目錄名

echo "/etc/sysconfig/" | sed 's@[^/]\+/\?$@@'

4.高級編輯命令

h:把模式空間中的內容覆蓋至保持空間中

H:把模式空間中的內容追加至保持空間中

g:從保持空間取出數據覆蓋至模式空間     get

G:從保持空間取出內容追加至模式空間    Get

x:把模式空間中的內容與保持空間中的內容進行互換

n:讀取匹配到的行的下一行至模式空間

N:追加匹配到的行的下一行至模式空間

d:刪除模式空間中的行

D:刪除多行模式空間中的所有行

示例

sed -n 'n;p' FILE:顯示偶數行

     

sed '1!G;h;$!d' FILE:逆向顯示文件內容

解析:

①第一個命令1!G

若取出的行不是文本中的第一行就將保持空間中的內容追加至模式空間,是第一行則不做任何操作

②第二個命令h

將模式空間的內容覆蓋至保持空間

③第三個命令$!d

若取出的行不是文本中的最後一行就將刪除模式空間中的最後一行,是文本中的最後一行則不做任何操作

P.S. 事實上使用tac命令即可實現同樣的功能

sed '$!N;$!D' FILE:取出文件後兩行

sed '$!d' FILE:取出文件最後一行

sed 'G' FILE:

sed '/^$/d;G' FILE:

sed 'n;d' FILE:顯示奇數行

sed -n '1!G;h;$p' FILE:逆向顯示文件中的每一行

三、awk

      awk:報告生成器,格式化文本輸出

AWK:Aho, Weinberger, Kernighan --> New AWK, NAWK

GNU awk, gawk

gawk - pattern scanning and processing language

基本用法:gawk  [options]  'program'  FILE ...

                  program:PATTERN{ACTION STATEMENTS}

                       語句之間用分號分隔

選項:

-F:指明輸入時用到的字段分隔符

-v var=value:自定義變量        P.S 每聲明一個變量時都要加-v選項

      程序模型:

      awk將輸入流看做一連串記錄的集合,每條記錄可細分爲多個字段

1.print

print item1, item2, ...

要點:

            ① 逗號分隔符

② 輸出的各item可以是字符串,也可以是數值;當前記錄的字段、變量或awk的表達式

③ 如省略item,相當於print $0;

2.變量

①內建變量

FS:input field seperator,輸入字段分割字符,默認爲空白字符

OFS:output field seperator,輸出字段分割字符,默認爲空白字符

RS:input record seperator,輸入記錄分割字符,即輸入時的換行符

ORS:output record seperator,輸出時的換行符

 

NF:number of field,字段數量

{print NF}, {print $NF}    在awk中引用變量時不需要加符號$(除了字段變量)

NR:number of record, 行數

            FNR各文件分別計數;行數

FILENAME:當前文件名

ARGC:命令行參數的個數

ARGV:數組,保存的是命令行所給定的各參數

②自定義變量

a.  -v var=value

P.S. 變量名區分字符大小寫,引用變量時不需要加符號$(除了字段)

b.  在program中直接定義

3.printf

格式化輸出:printf FORMAT, item1, item2, ...

① FORMAT格式符必須給出

② 不會自動換行,需要顯式給出換行控制符,\n

            ③ FORMAT中需要分別爲後面的每個item指定一個格式化符號

      格式符:

            %c:顯示字符的ASCII碼

            %d, %i:顯示十進制整數

            %e, %E:科學計數法數值顯示

            %f:顯示爲浮點數

            %g, %G:以科學計數法或浮點形式顯示數值

            %s:顯示字符串

            %u:無符號整數

            %%:顯示%自身

修飾符:

#[.#]:第一個數字控制顯示的寬度;第二個#表示小數點後的精度

                                   %3.1f

            -:左對齊

            +:顯示數值的符號

4.操作符

      算術操作符:

                        x+y, x-y, x*y, x/y, x^y, x%y

                        -x:轉換爲數值

                        +x:轉換爲數值

字符串操作符:沒有符號的操作符,字符串連接

賦值操作符:

                       =, +=, -=, *=, /=, %=, ^=

                       ++, --

比較操作符:

                       >, >=, <, <=, !=, ==

模式匹配符:

                       ~:是否匹配

                       !~:是否不匹配

邏輯操作符:

                       &&

                       ||

                       !

函數調用:

                       function_name(argu1, argu2, ...)

條件表達式:

                       selector?if-true-expression:if-false-expression

# awk -F: '{$3>=1000?usertype="Common User":usertype="Sysadmin or SysUser"; printf "%15s:%-s\n",$1,usertype}' /etc/passwd

5.PATTERN

      ① empty:空模式,匹配每一行

      ② /regular expression/:僅處理能夠被此處的模式匹配到的行

      ③ relational expression:關係表達式;結果有“真”有“假”;結果爲“真”纔會被處理

            真:結果爲非0值,非空字符串

      ④ line ranges:行範圍

            startline,endline:/pat1/,/pat2/

            注意: 不支持直接給出數字的格式

      ~]# awk -F:'(NR>=2&&NR<=10){print $1}' /etc/passwd

      ⑤ BEGIN/END模式

            BEGIN{}:僅在開始處理文件中的文本之前執行一次

            常見用法:

                  變量初始化、打印輸出表頭

            END{}:僅在文本處理完成之後執行一次

            常見用法:

                  打印所有行的分析結果

# awk -F: 'BEGIN{printf "%10s  %10s\n--------------------------\n","username","UID"}{printf "%10s %10s\n",$1,$3}' /etc/passwd

6.常用的action

      ① Expressions

      ② Control statements:if, while等

      ③ Compound statements:組合語句

      ④ input statements

      ⑤ output statements

7.控制語句

條件式執行

      if(condition) {statments}

      if(condition) {statments} else {statements}

重複執行

      while(conditon) {statments}

      do {statements} while(condition)

      for(expr1;expr2;expr3) {statements}

      break

      continue

 

      delete array[index]

      delete array

 

      exit

      { statements }

①if-else

語法:if(condition) statement [else statement]

~]# awk -F:'{if($3>=1000) {printf "Common user:%s\n",$1} else {printf "root or Sysuser:%s\n",$1}}' /etc/passwd

~]# awk -F:'{if($NF=="/bin/bash") print $1}' /etc/passwd

~]# awk '{if(NF>5) print $0}' /etc/fstab

~]# df -h | awk -F[%] '/^\/dev/{print $1}' | awk '{if($NF>=20) print $1}'

使用場景:對awk取得的整行或某個字段做條件判斷

②while循環

語法:while(condition) statement

條件“真”,進入循環;條件“假”,退出循環

使用場景:對一行內的多個字段逐一類似處理時使用;對數組中的各元素逐一處理時使用

~]# awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {print $i,length($i); i++}}' /etc/grub2.cfg

      匹配/etc/grub2.cfg以若干空格開頭後接linux16的行,打印其中每個字段及其字符個數

~]# awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {if(length($i)>=7) {print $i,length($i)}; i++}}' /etc/grub2.cfg

③do-while循環

語法:do statement while(condition)

意義:至少執行一次循環體

④for循環

語法:for(expr1;expr2;expr3) statement

for(variable assignment;condition;iteration process) {for-body}

~]# awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg

特殊用法:

能夠遍歷數組中的元素

                  語法:for(var in array) {for-body}

⑤switch語句

語法:switch(expression) {case VALUE1 or /REGEXP/:statement; case VALUE2 or /REGEXP2/:statement; ...; default:statement}

⑥break和continue

break [n]

continue    提前結束本來循環,進入下一字段

⑦next

      awk實際上進行了兩次循環

next可以提前結束對本行的處理而直接進入下一行

~]# awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd

8.array

關聯數組:array[“index-expression”]

index-expression:

①可使用任意字符串;字符串要使用雙引號

②如果某數組元素事先不存在,在引用時,awk會自動創建此元素,並將其值初始化爲“空串”

P.S. 若要判斷數組中是否存在某元素,要使用"index in array"格式進行

若要遍歷數組中的每個元素,要使用for循環

                             for(var in array) {for-body}

~]# awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print weekdays[i]}}'  和bash不一樣,索引記得不要加$

            P.S. var會遍歷array的每個索引

常見用法:統計某一類值出現的次數

~]# netstat -tan | awk '/^tcp\>/{state[$NF]++}END{for(i in state) { print i,state[i]}}'

                       state["LISTEN"]++

                       state["ESTABLISHED"]++

~]# awk '{ip[$1]++}END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log

練習1:統計/etc/fstab文件中每個文件系統類型出現的次數

~]# awk '/^UUID/{fs[$3]++}END{for(i in fs) {print i,fs[i]}}' /etc/fstab

練習2:統計指定文件中每個單詞出現的次數

要做行內字段遍歷

~]# awk '{for(i=1;i<=NF;i++){count[$i]++}}END{for(i in count) {print i,count[i]}}' /etc/fstab

9.函數

①內置函數

數值處理:

rand():返回0和1之間一個隨機數

字符串處理:

length([s]):返回指定字符串的長度

sub(r,s,[t]):以r表示的模式來查找t所表示的字符中的匹配的內容,並將其第一次出現替換爲s所表示的內容

gsub(r,s,[t]):以r表示的模式來查找t所表示的字符中的匹配的內容,並將其所有出現均替換爲s所表示的內容

split(s,a[,r]):以r爲分隔符切割字符s,並將切割後的結果保存至a所表示的數組(從1開始編號)

~]# netstat -tan | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for (i in count) {print i,count[i]}}'

②自定義函數

function name(arg1, arg2, arg3..)

{

      statement

}

回顧

grep

      grep:文本過濾器

            PATTERN

            REGEXP:

                  BRE、ERE

            BRE:

                  字符匹配:., [], [^]

                  次數匹配:*, \?, \+, \{m\}, \{m,n\}

                  位置錨定:^, $, \<, \b, \>, \b

                  分組:\(\)

                       後向引用:\1, \2, ...

            vim, sed, awk, nginx,

egrep及擴展的正則表達式

      egrep = grep -E

      egrep [OPTIONS] PATTERN [FILE...]

      擴展正則表達式的元字符:

            字符匹配:

                  .

                  []

                  [^]

            次數匹配:

                  *

                  ?:0或1次;

                  +:1次或多次;

                  {m}:匹配m次;

                  {m,n}:至少m,至多n次;

            錨定:

                  ^

                  $

                  \<, \b

                  \>, \b

            分組:

                  ()

                  後向引用:\1, \2, ...

            或者:

                  a|b

                       C|cat:C或cat

sed

      sed [options] 'SCRIPT' FILE...

      編輯命令:d, p, w, r, a, i, c, s, =,

                       n, N, h, H, g, G, p, P, x, D

awk

      -F, -v

      print, printf

      $i, FS, RS, NF, NR, ARGC, ARGV

      +, -, *, /, ^, %

      >, <, ==, !=

      ~, !~

      &&, ||, !

      BEGIN, END

      for( var in array )

      split(s,a[,r])

參考資料:

①馬哥隨堂筆記

②《Linux Shell 腳本攻略》

③《Shell 腳本學習指南》

注:誠懇歡迎讀者對本文提出批評意見,若發現存在錯誤,我定第一時間修改。如果讀者覺得文章對您有幫助,歡迎點贊鼓勵一下喲٩(๑❛ᴗ❛๑)۶。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章