iT邦幫忙

2021 iThome 鐵人賽

DAY 13
1
Software Development

系統與服務雜談系列 第 13

sed - 5 Replace command

前篇回顧
sed - 簡介 讀取編輯文字檔的好工具
sed - 2 Pattern
sed - 3 Delete command
sed - 4 Write commands

今天聊sed的修改或取代操作

  • s 取代指定匹配到的字串, 變成新字串

取代

基本格式

# 取代指定匹配到的字串, 變成新字串
sed 's/old/new/' filename
# 多組需要取代的pattern
sed -e 's/old/new/' -e 's/old/new/' filename

# 取代指定regex匹配到的字串, 變成新字串
sed 's/Regex/new' filename

# -r, 支持擴展Regexp, 取代指定regex匹配到的字串, 變成新字串
sed -r 's/Extended Regex/new' filename

重要

首先拿/etc/passwd來玩, 但是我們玩備份的這檔案, 別玩etc底下的, 玩完系統就崩潰了

# 複製passwd 到當前目錄
cp /etc/passwd ./

目標把passwd裡面剛好前三個字母的字串給替換成AAA
https://ithelp.ithome.com.tw/upload/images/20210919/20104930ReEunwBZz4.png

head -5 passwd | sed 's/.../AAA/'      
> AAAt:x:0:0:root:/root:/bin/bash
> AAAmon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
> AAA:x:2:2:bin:/bin:/usr/sbin/nologin
> AAA:x:3:3:sys:/dev:/usr/sbin/nologin
> AAAc:x:4:65534:sync:/bin:/bin/sync

這裡有點懶, 透過head工具取前5行, 透過管線傳給sed,
...這裡的三個點, 不是無言已對, .是regex的任意符號(什麼都匹配就是不匹配換行符號)
3個.就是三個任意符號, 但若是該行不足3個字元, 就不會被匹配到
拿之前的os.txt做示範, 可以看到最後一行只有OS兩個字元, 就不會被匹配到

sed 's/...//' os.txt 
> dows OS	os
> ux Os os
> roid
> OS

其實取代也能是種刪除, 把匹配到的給取代成空字串, 也是種刪除
跟上面相比, 前三個字母就不見了, 因為被取代成空字串

head -5 passwd | sed 's/...//'   
> t:x:0:0:root:/root:/bin/bash
> mon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
> :x:2:2:bin:/bin:/usr/sbin/nologin
> :x:3:3:sys:/dev:/usr/sbin/nologin
> c:x:4:65534:sync:/bin:/bin/sync

來把sbin給取代成空字串看看, 這裡用s*bin, *表示s可以出現任意次數

head -5 passwd | sed 's/s*bin//'
> root:x:0:0:root:/root://bash
> daemon:x:1:1:daemon:/usr/:/usr/sbin/nologin
> :x:2:2:bin:/bin:/usr/sbin/nologin
> sys:x:3:3:sys:/dev:/usr//nologin
> sync:x:4:65534:sync:/:/bin/sync

可以發現第2,3行, 後面還有sbin沒被取代, 第5行還有一個bin
這是因為sed預設只匹配第一個
Regex使用g能做全域匹配, 這裡sed是以行為單位, 所以這裡的全域, 就是一整行都進行查找匹配.
後面的很多sbin/bin就都被取代了

head -5 passwd | sed 's/s*bin//g'
> root:x:0:0:root:/root://bash
> daemon:x:1:1:daemon:/usr/:/usr//nologin
> :x:2:2::/:/usr//nologin
> sys:x:3:3:sys:/dev:/usr//nologin
> sync:x:4:65534:sync:/://sync

當同一行匹配被匹配多次時, 指定第幾次出現才進行替換; 記住默認是取代第一次匹配成功的
只針對同一行第2次匹配出現的進行取代

# 這裡搭配-n不進行pattern space輸出
# p, 只輸出被匹配的, 這樣畫面乾淨點
head -5 passwd | sed -n 's/root/!!!/2p'
> root:x:0:0:!!!:/root:/bin/bash

搭配之前的w把匹配的寫到外部檔案

head -5 passwd | sed -n 's/root/!!!/2pw /tmp/demo.txt'
> root:x:0:0:!!!:/root:/bin/bash

cat /tmp/demo.txt
> root:x:0:0:!!!:/root:/bin/bash

也能搭配之前的行定位

# 只針對1~3行做匹配
head -5 passwd | sed -n '1,3s/s*bin/!!!/p'
> root:x:0:0:root:/root:/!!!/bash
> daemon:x:1:1:daemon:/usr/!!!:/usr/sbin/nologin
> !!!:x:2:2:bin:/bin:/usr/sbin/nologin

sed的行定位, 其實不只能指定行號, 也能用Regex
針對有usr的行,做s*bin的替換

head -5 passwd | sed -n '/usr/s/s*bin/!!!/p'
> daemon:x:1:1:daemon:/usr/!!!:/usr/sbin/nologin
> !!!:x:2:2:bin:/bin:/usr/sbin/nologin
> sys:x:3:3:sys:/dev:/usr/!!!/nologin

其中的pattern也不是只能一組, 也能多組pattern
一種是之前提到的-e, 格式則變成-e 's/old/new/' s/old/new/'
也能用;作多組的分隔
格式變成行定位{s/old/new/;s/old/new/}
如果行定位是Regex則是/Regex/{s/old/new/;s/old/new/}

# 針對第1~3行, 把bin取代成!!!
# 針對第1~3行, 把usr取代成***
head -5 passwd | sed -r '1,3s/bin/!!!/;1,3s/usr/***/'      
> root:x:0:0:root:/root:/!!!/bash
> daemon:x:1:1:daemon:/***/s!!!:/usr/sbin/nologin
> !!!:x:2:2:bin:/bin:/***/sbin/nologin
> sys:x:3:3:sys:/dev:/usr/sbin/nologin
> sync:x:4:65534:sync:/bin:/bin/sync

head -5 passwd | sed -r -e '1,3s/bin/!!!/' -e '1,3s/usr/***/'  
> root:x:0:0:root:/root:/!!!/bash
> daemon:x:1:1:daemon:/***/s!!!:/usr/sbin/nologin
> !!!:x:2:2:bin:/bin:/***/sbin/nologin
> sys:x:3:3:sys:/dev:/usr/sbin/nologin
> sync:x:4:65534:sync:/bin:/bin/sync

# 針對第1~3行, 把bin取代成!!!
# 把有nologin的行給delete
head -5 passwd | sed -r -e '1,3s/bin/!!!/;/nologin/d'
> root:x:0:0:root:/root:/!!!/bash
> sync:x:4:65534:sync:/bin:/bin/sync

head -5 passwd | sed -r -e '1,3s/bin/!!!/' -e '/nologin/d'
> root:x:0:0:root:/root:/!!!/bash
> sync:x:4:65534:sync:/bin:/bin/sync

今日小結

今天介紹的是基本的sed修改操作,
能發現不管是grep、awk、sed, 想再實戰中找到我們想要的行跟位置, 還是需要Regex基本的熟練度.

----參考來源
Regular-Expressions .Dot


上一篇
sed - 4 Write commands
下一篇
sed - 6 Hold Space簡介
系列文
系統與服務雜談32

尚未有邦友留言

立即登入留言