文本處理三劍客之sed

文本處理三劍客之sed


  • sed概述

  • sed使用示例

  • sed的高級應用


sed概述

sed, 作爲文本三劍客之一,其定位就是一個編輯器, 而且sed是一個流式編輯器(stream editor),其主要功能是過濾和轉換文本。

sed - stream editor for filtering and transforming text

作爲一個強大的文本處理功能,sed 自然能夠配合正則表達式,另外,所謂流編輯器,sed 是逐行地讀取文本,在文本行中應用指定的命令,且默認輸出到stdout; sed 擁有兩個緩衝區,活躍模式空間(active pattern space)與輔助保持空間(auxiliary hold space), 簡稱爲模式空間與保持空間,且這兩個空間默認都爲空。

`sed' maintains two data buffers: the active _pattern_ space, 
and theauxiliary _hold_ space. Both are initially empty.

sed的基本應用只用到模式空間就夠了,sed的高級應用需要模式空間與保持空間的想到配合。在sed 的基本應用中,sed會把當前處理的行存儲在一個臨時緩衝區,即模式空間;然後sed會在模式空間中處理文本,並且傳送到標準輸出,再接着處理下一行,如此循環往復,直至文件末尾,這就是sed的基本工作原理。由此可風,sed在默認情況下是不修改原文件的;在sed的高級應用中,多了一個保持空間,保持空間是與模式空間進行交互的,且以模式空間爲主,保持空間爲輔,sed匹配的內容最終都得經由模式空間傳輸至stdout.

常用參數

  1. -n: --quiet, --silent, 靜默模式,不輸出模式空間內容的自動打印

  2. -e: --expression=script, 多點編輯,類似於管道

  3. -f: --file=script-file, 從指定文件中讀取sed腳本

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

  5. -i: 原處編輯,-i.bak可同時作備份

sed地址定界

所謂sed的地址定界,就是我們對要處理的文本給出一個範圍,然後讓sed只針對此範圍去做相應的處理。

  1. 不給地址,默認全文處理

  2. 單地址:

    #:指定的行
     /pattern/:被此模式所能匹配到的每一行
  3. 地址範圍:

    #,#:從多少行到多少行
     #,+#:從第#行起至第#行+#行結束(相對地址)
     #,/part/: 從第#行開始,到第一次匹配到/part/模式結束
     /part1/,/part2/: 從第一次匹配到/part1/開始,到最後一次匹配到/part2/結束
  4. 步進: ~

       
        1~2:奇數行
        2~2: 偶數行

sed編輯命令

  1. d: 刪除模式空間匹配到的行

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

  3. a \test: 在行後面追加文本,\可用空格替代;支持使用\n 實現多行追加

  4. i \test: 在行前面插入文本,\可用空格替代;支持使用\n 實現多行插入

  5. c \test: 替換爲單選或多行文本,\可用空格替代

  6. w /PATH/TO/SOMEFILE: 另存爲模式匹配的行到指定文件

  7. r /PATH/TO/SOMEFILE: 讀取指定文件的文本至模式空間匹配到的行後

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

  9. !: 模式空間匹配的行作反處理

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

替換標記:
            g: 行內全局替換
            p: 顯示替換成功的行
            w /PATH/TO/SOMEFILE:將替換成功的行保存至指定的文件中


sed使用示例

創建一個測試文件:

[root@centos7 ~#]cat f1
1
2
3
4
5
  1. 刪除f1中的空格並寫入原文件

[root@centos7 ~#]sed -i '/^$/d' f1

wKiom1erEDvSleAdAABC4VPEYmQ943.png

  1. 刪除1-3行

[root@centos7 ~#]sed '1,3d' f1
4
5
[root@centos7 ~#]
  1. 打印出第2行,並說明默認打印模式空間的意義

[root@centos7 ~#]sed -n '2p' f1   # 只打印出了第2行
2
[root@centos7 ~#][root@centos7 ~#]sed '2p' f1
1
2                               # sed 默認顯示出模式空間中的2
2                               # sed 匹配到第2行並打印出
3
4
5
  1. 給第2行後加入‘liansir’字樣,在第3行前加入‘xiaolei'字樣,替換第4行爲’lvxing'

[root@centos7 ~#]nl f1 |sed -e '2a \liansir' -e '3i \xiaolei' -e '4c \lvxing' 
     1  1           # 命令n: nl - number lines of files
     2  2
liansir
xiaolei     
     3  3
lvxing     
     5  5
[root@centos7 ~#]
  1. 給f1加入空格後並顯示空格行號

[root@centos7 ~#]sed -n '/^$/=' f1

wKioL1erEYfhxMeiAAAxw1iZk_A483.png

  1. 給數字2後加'liansir'字樣,給數字3前加'xiaolei'字樣

[root@centos7 ~#]sed -e 's/2/&liansir/' -e 's/3/xiaolei&/' f1

wKioL1erJifjoTt9AAAyaxK3ZTI975.png

  1. 刪除/etc/grub2.cfg文件中所有以空白開頭的行行首的空白字符

[root@centos7 ~#]sed -r 's/^[[:space:]]+//' /etc/grub2.cfg   # 以“替換”的方式實現了“刪除”,-r 表示使用擴展正則表達式

wKioL1erJonjH9eRAAB3iSDcYtk073.png

wKiom1erJpyx_RNjAAAxw1iZk_A118.png

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

[root@centos7 ~#]sed -r 's/^#[[:space:]]*//' /etc/fstab

wKiom1erJqySxaeQAACjBTSSyu4251.png

wKioL1erJseiBuAgAACjjU_TG1s241.png

  1. 在/root/install.log每一行行首增加#號

[root@centos6 ~#]sed 's/^/#/' /root/install.log
或[root@centos6 ~#]sed -r 's/(.*)/#\1/' /root/install.log  # 後向引用,注意使用擴展正則表達式
或[root@centos6 ~#]sed -r 's/(.*)/#&/' /root/install.log

wKioL1erJuaA8zJNAACRixUhHXU596.png

  1. 處理/etc/fstab路徑,使用sed命令取出其目錄名和基名 (此題有料,衆多解法,有興趣者探索之)

取基名:下面命令的主要思路是不管前面是什麼,只錨定最後一個單詞的開頭

[root@centos6 ~#]echo '/etc/fstab' |sed -r 's/(.*)\/<//'  # 注意轉義
/etc/fstab
[root@centos6 ~#]
[root@centos6 ~#]echo '/etc/fstab' |sed -r 's@(.*)/<@@'  # 不想轉義可直接換分隔符
/etc/fstab
[root@centos6 ~#]echo '/etc/fstab/' |sed -r 's@(.*)/<@@' # 當基名爲一個目錄時照樣適用
/etc/fstab/
[root@centos6 ~#]

取目錄名:着眼點還是在後面的基名上,即刪除了基名

[root@centos6 ~#]echo '/etc/fstab' |sed -r 's@[^/]+/?$@@'
/etc/
[root@centos6 ~#]echo '/etc/' |sed -r 's@[^/]+/?$@@'     
/
[root@centos6 ~#]

取目錄名與基名的完美解法

[root@centos6 ~#]echo /etc/fstab |sed -r 's@(.*/)([^/]+/?)$@\1@'  # 取目錄名
/etc/
[root@centos6 ~#]echo /etc/fstab |sed -r 's@(.*/)([^/]+/?)$@\2@'  # 取基名
fstab
[root@centos6 ~#]

完美之外:

1. 涉及到/ 時則換分隔符,避免轉義,如@
2. 將目錄名與基名以分組的形式分別表示出來,表達了一個完整的路徑
3. 分別後向引用則可取得目錄名或基名,屬於通用解法
  1. 利用sed 取出ifconfig命令中本機的IPv4地址 # 先打印出本機IP所在行,然後"掐頭去尾"。

[root@centos7 ~#]ifconfig |sed -n '2p' |sed -e 's/.*inet//' -e 's/net.*//'
 10.1.253.100  
[root@centos7 ~#]
  1. 統計centos安裝光盤中Package目錄下的所有rpm文件的以.分隔倒數第二個字段的重複次數

[root@centos6 /media/CentOS_6.8_Final/Packages#]ls |sed -r 's@.*\.(.*)\.rpm$@\1@' |sort |uniq -c
      4 i686    
      919 noarch      
      1 TRANS.TBL   
      2283 x86_64
[root@centos6 /media/CentOS_6.8_Final/Packages#]
# 注意\.是轉義.

至此,我們再來了解下sed的高級應用!

sed高級編輯命令:

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

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

  3. g: 從保持空間讀取數據覆蓋至模式空間中

  4. G: 從保持空間讀取數據追加至模式空間中

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

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

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

  8. d: 刪除模式空間中的行

  9. D: 刪除當前模式空間開端至\n的內容(不再傳至標準輸出),放棄之後的命令,但是對剩餘模式空間重新執行sed.

h H    Copy/append pattern space to hold space.
g G    Copy/append hold space to pattern space.
x      Exchange the contents of the hold and pattern spaces.
n N    Read/append the next line of input into the pattern space.
d      Delete pattern space.  Start next cycle.

可見,模式空間與保持空間的交互命令有:h, H, g, G, x.其流向關係可用下圖表示:

wKioL1erJ2yjr5zhAAAhKP-QJ_c512.png

sed高級應用示例

創建一個測試文件:

[root@centos7 ~#]echo {1..5} |tr ' ' '\n' >f1
[root@centos7 ~#]cat f1
1
2
3
4
5
[root@centos7 ~#]
  1. 輸出偶數行與奇數行:

普通用法:
[root@centos7 ~#]sed -n '1~2p' f1
1
3
5
[root@centos7 ~#]sed -n '2~2p' f1
2
4
[root@centos7 ~#]
高級用法:
[root@centos7 ~#]sed 'n;d' f1   
1
3
5
[root@centos7 ~#]sed -n 'n;p' f1
2
4
[root@centos7 ~#]

高級用法解析:

~#]sed 'n;d' f1
    首先,sed讀入第一行的1到模式空間,n動作匹配到第二行中的2並覆蓋掉
    模式空間中的1,此時模式空間中僅有2,又因爲d動作被刪除了,模式空間全
    空了!那怎麼又輸出1了呢?這是因爲sed 沒有使用-n選項,故1在被2覆蓋之
    前已被sed默認輸出,而2在模式空間卻被d給幹掉了,故而屏幕上輸出了1。
    然後sed雙讀入第三行中的3,被n匹配至其下一行中的4並讀入模式空間,但
    在4覆蓋3之前sed已默認打印出3,而4卻“胎死腹中”,故屏幕雙留下了3.

~#]sed -n 'n;p' f1 
    首先,sed 讀入第一行的1到模式空間,因爲n動作,又匹配到第二行中的2,
    並把2讀入模式空間覆蓋掉之前的1,模式空間中的內容輸入至stdout而得到2.
    然後,sed讀入第三行中的3到模式空間,因爲n動作,匹配到其下一行中的4,
    並把4讀入模式空間並覆蓋掉之前的3並p至stdout.
  1. 倒序輸出:

[root@centos7 ~#]sed '1!G;h;$!d' f1
5
4
3
2
1
[root@centos7 ~#]

高級用法解析

~#]sed '1!G;h;$!d' f1
    這個sed命令,整體被分別;分隔爲三段,我們認爲他有三個動作:
    1!G;  如果不是第1行,則把保持空間中的內容追加至模式空間(保持空間默認爲空)    
    h;    把模式空間中的內容覆蓋至保持空間
    $!d   如果不是最後一行則刪除

結合sed流編輯器的特點,逐行讀入,意味每讀入一行便會執行上面的三個動作;
每一行:
1. sed讀入第一行中的1到模式空間,由於不符合'1!'這個條件,則跳過G執行h;
2. h 把模式空間中的1覆蓋至保持空間;(此時模式空間中爲1,保持空間中爲1)
3. 1不是最後一行,符合條件'$!',刪除(此時模式空間爲空,保持空間都爲1)
第二行:
1. sed讀入第二中的2至模式空間,符合'1!'這個條件,從而執行G動作,G把
保持空間中的1追加至模式空間;
於是,此時模式空間中的內容爲:
2
1
但保持空間是1
2. h 把模式空間中的內容覆蓋至保持空間
3. 執行 $!d(模式空間爲空)

依次類推,當執行完第四步時,情況是這樣的:模式空間爲空,保持空間的內容爲:
4
3
2
1
第五步:
1. sed讀入第五行中的內容5至模式空間,符合'1!'這個條件,執行G動作,把
保持空間中的內容追加到模式空間
2. h 把模式空間中的內容覆蓋至保持空間
3. 第五行是最後一行,不符合'$!',跳過$!d於是,屏幕上顯示:
5
4
3
2
1

一個sed的高級命令,應得大費如此篇幅,目的就是更爲深入地理解sed這個流編輯器,逐行處理文本,有兩個模式空間,一主一輔。看下圖(圖片來自網友),就更能清楚地理解sed的工作原理了。

wKioL1erKAbClxluAAB_8W01-iY715.png

此圖僅供參考!

  1. 一鍋上了先

[root@centos7 ~#]sed '$!N;$!D' f1
4
5
[root@centos7 ~#]
[root@centos7 ~#]sed '$!d' f1  # 不是最後一行則刪除,意味着最後一行保留輸出
5
[root@centos7 ~#]
[root@centos7 ~#]sed 'G' f1   # 保留空間默認爲空
1

2

3

4

5
[root@centos7 ~#]

[root@centos7 ~#]sed 'g' f1





[root@centos7 ~#]sed 'g' f1 |wc -l  # 可見輸出了5個空格
5
[root@centos7 ~#]
[root@centos7 ~#]sed '/^$/d;G' f1
1
2
3
4
5
[root@centos7 ~#]
[root@centos7 ~#]sed -n '1!G;h;$p' f1  # 看與高級應用分析與區別
5
4
3
2
1
[root@centos7 ~#]
4. 交換模式空間與保持空間的內容
[root@centos7 ~#]sed 'x' f1  # 模式空間中最後交換了一行被保留空間中第一個空白替換了

1
2
3
4
[root@centos7 ~#]

sed 的功能如此強大,掌握也非一日之功,sed可以幫助我們自動編輯一個或多個文件,簡化對文件的反覆操作,編寫程序轉換等。


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