Shell腳本之sed篇
目錄:
一、概述
二、sed基本文法格式
三、簡單正規表達式
四、sed腳本指令(注釋、替換、删除、追加、列印、下一行、讀寫檔案、退出、多行模式)
五、綜合案例
1.sed是一款流編輯工具,用來對文本進行過濾與替換工作,特别是當你想要對幾十個配置檔案做統計修改時,你會感受到sed的魅力!
sed通過輸入讀取檔案内容,但一次僅讀取一行内容進行某些指令處理後輸出,是以sed更适合于處理大資料檔案。
2.sed流程:
* 通過檔案或管道讀取檔案内容。
* sed并不直接修改源檔案,而是将讀入的内容複制到緩沖區中,我們稱之為模式空間(pattern space)。
* 根據sed的指令對模式空間中的内容進行處理并輸出結果,預設輸出至标準輸出即螢幕上。
輸入
------------------------------------------------
|
v
讀取一行内容,并複制到模式空間 <------------ sed 指令
輸出經過處理後的内容
*****************************************************************************************************
二、sed基本文法結構
sed Options... [script] [inputfile...]
sed 選項... [腳本指令] [輸入檔案]
如果沒有輸入檔案,則sed預設對标準輸入進行處理(即鍵盤輸入)。腳本指令是第一個不以“-”開始的參數。
1.選項含義:
--version 顯示sed版本。
--help 顯示幫助文檔。
-n,--quiet,--silent 靜默輸出,預設情況下,sed程式在所有的腳本指令執行完畢後,将自動列印
模式空間中的内容,這些選項可以屏蔽自動列印。
-e script 允許多個腳本指令被執行。
-f script-file,
--file=script-file 從檔案中讀取腳本指令,對編寫自動腳本程式來說很棒!
-i,--in-place 直接修改源檔案,經過腳本指令處理後的内容将被輸出至源檔案(源檔案被修改)
慎用!
-l N, --line-length=N 該選項指定l指令可以輸出的行長度,l指令用于輸出非列印字元。
--posix 禁用GNU sed擴充功能。
-r, --regexp-extended
在腳本指令中使用擴充正規表達式
-s, --separate 預設情況下,sed将把指令行指定的多個檔案名作為一個長的連續的輸入流。
而GNU sed則允許把他們當作單獨的檔案,
這樣如正規表達式則不進行跨檔案比對。
-u, --unbuffered 最低限度的緩存輸入與輸出。
2.簡單案例:
以上僅是sed程式本身的選項功能說明,至于具體的腳本指令(即對檔案内容做的操作)後面我們會較長的描述,
這裡就簡單介紹幾個腳本指令操作作為sed程式的例子。
a,append 追加
i,insert 插入
d,delete 删除
s,substitution 替換
案例說明:灰色背景的内容為待處理的源檔案,紅色字型的文字為sed腳本,藍色字型的文字為處理後的結果輸出。
這裡test.txt為樣本檔案:
[jacob@localhost ~] #cat test.txt
DEVICE=eth0
ONBOOT=yes
BOOTPROTO=static
IPADDR=192.168.0.1
NETMASK=255.255.255.0
GATEWAY=192.168.0.254
[jacob@localhost ~] #sed '2a TYPE=Ethernet' test.txt 第二行後添加TYPE=Ethernet
[jacob@localhost ~] #sed '3i TYPE=Ethernet' test.txt 第三行前添加TYPE=Ethernet
[jacob@localhost ~] #sed 's/yes/no/g' test.txt 将樣本檔案中的所有yes替換為no
[jacob@localhost ~] #sed '3,4d' test.txt 删除第3至4行的内容
以上大多數操作指令,都依據行号定位操作對象(位址),如:2a即第二行後添加。
但實際情況可能大多數情況你并不确定你要操作對象(位址)的行号,這時更多的我們會使用正規表達式确定操作對象(位址)。
下面是使用正規表達式定位操作行的示例:
[jacob@localhost ~] #sed '/ONBOOT/a TYPE=Ethernet' test.txt
比對到包含ONBOOT的行,并在其後添加TYPE=Ethernet
[jacob@localhost ~] #sed '/^GATEWAY/d' test.txt
比對以GATEWAY開始的行,并删除改行
另外我們的操作指令可以,寫入到腳本檔案中并通過sed的-f選項讀取。
建立一個sed腳本,内容如下:
[jacob@localhost ~] #cat sed.sh
/^$/d 這條指令的作用是:比對到空白行後,删除改行。
[jacob@localhost ~] #sed -f sed.sh test.txt 對test.txt檔案執行sed.sh指令
另外,當你需要執行多個指令時,可以使用以下三種方法:
1. [jacob@localhost ~] #sed 's/yes/no/;s/static/dhcp/' test.txt 注:使用分号隔開指令。
2. [jacob@localhost ~] #sed -e 's/yes/no/' -e 's/static/dhcp/' test.txt 注:使用-e選項。
3. [jacob@localhost ~] #sed '
>s/yes/no/
>s/static/dhcp/' test.txt 注:利用分行指令。
然而在指令行上輸入過長的指令是愚蠢的,這時就需要-f選項指定sed腳本檔案,在腳本檔案中可以包含多行指令,而且便于修改!
*****************************************************************************************
從以上案例中我們不難發現,我們編寫的腳本指令需要指定一個位址來決定操作範圍,如果不指定則預設對檔案的所有行操作。
如:sed 'd' test.txt 将删除test.txt的所有行,而'2d'則僅删除第二行。
1.為sed指令确定操作位址:
number 指定輸入檔案的唯一行号。
first~step 以first開始,并指定操作步長為step,如1~2,指定第一行,第三行,第五行... 為操作位址。
[jacob@localhost ~] #sed -n '1~2p' test.txt 列印檔案的奇數行。
2~5,則可以指定第二行開始,每5行比對一次操作位址。
$ 比對檔案的最後一行。
/regexp/ //中間包含的是正規表達式,通過正規表達式比對操作位址。
注://空的正規表達式,比對最近一次正規表達式的比對位址,會在後面使用看出效果。
\cregexpc 比對擴充正規表達式,c字元可以使用任意字元替代。
addr1,addr2 比對從操作位址1到操作位址2的所有行。
[jacob@localhost ~] #sed '2,8d' test.txt 删除2至8中間的所有行。
addr1,+N 比對位址1以及後面的N行内容。
2.正規表達式概述(對你要找内容的一種描述)
char 字元本身就比對字元本身,如/abc/就是定位包含abc的行。
* 比對前面表達式出現了0或若幹次,如/a*/可以幫你找到a,aa,aaa,... ...等等。
\+ 類似于*,但比對前面表達式的1次或多次,這屬于擴充正規表達式。
\? 類似于*,但比對前面表達式的0次或1次,這屬于擴充正規表達式。
\{i\} 類似于*,但比對前面表達式的i次(i為整數),如:a\{3\}可以幫你找到aaa。
\{i,j\} 比對前面表達式的i到j次,如a\{1,2\}可以幫你找到a或aa或aaa。
\{i,\} 比對前面表達式至少i次。
\( \) 将\( \)内的模式存儲在保留白間。最多可以存儲9個獨立子模式,可
通過轉義\1至\9重複保留白間的内容至此點。
\n 轉義\1至\9重複保留白間的内容至此點。
例:test.txt的内容為ssttss
grep '\(ss\)tt\1' test.txt \1表示将ss重複在tt後面
該grep指令等同于grep ssttss test.txt 在test.txt檔案中找ssttss
. (點)比對任意字元。
^ 比對行的開始,如^test 将比對所有以test開始的行。
$ 比對行的結尾,如test$ 将比對所有以test結尾的行。
[] 比對括号中的任意單個字元,如a[nt] 将比對an或at。
[^] 比對不包含在[]中的字元,如[^a-z] 将比對除a-z以外的字元。
\n 比對換行符。
\char 轉義特殊字元,如\*,就是比對字面意義上的星号。
**************************************************************************************
四、sed腳本指令
常用的sed腳本指令有:
*注釋(#)
*替換(s)
*删除(d)
*追加(a)
*插入(i)
*更改(c)
*列印(l)
*轉換(y)
*列印(p)
*讀寫檔案(r,w)
*退出(q)
*下一步(n)
*Next(N)
*Print(P)
*Delete(D)
*Hold(h,H)
*Get(g,G)
*branch,test
sed腳本指令的基本格式:
[address]command
[位址]指令 備注:有些指令僅可以對一行操作,有些可以對多行操作。
指令可以用大括号進行組合以使指令序列可以作用于同一個位址:
address{
command1
command2
command3
}
第一個指令可以和左大括号在同一行,但右大括号必須單獨處于一行。
注意事項:指令後添加空格會産生錯誤。
1.注釋(#):
注釋行是以#開始的行,如果#後面的字元為n,則屏蔽sed程式的自動輸出功能,等同于指令選項-n。
2.替換(s,Substitution):
[address]s/pattern/replacement/flags
address為操作位址,s為替換指令,/pattern/比對要替換的内容,/replacement/為替換的内容。
flags可以是:
n 1至512之間的數字,表示對模式空中指定模式的第n次出現進行替換。
如一行中有3個A,而隻想替換第二個A。
g 對模式空間的比對進行全局更改。沒有g則僅第一次比對被替換,如一行中有3個A,則僅替換第一個A。
p 列印模式空間的内容。
w file
将模式空間的内容寫到檔案file中。
replacement為字元串,用來替換與正規表達式比對的内容。
在replacement部分,隻有下列字元有特殊含義:
& 用正規表達式比對的内容進行替換。
\n 比對第n個子串,這個子串之前在pattern中用\( \)指定。
\ 轉義(轉義替換部分包含的:&, \等)。
-----------------------------------------------------------------------------------------------
示例1所使用的樣本檔案為:
<html>
<title>First Web</title>
<body>Hello the World! <body>
</html>
示例1:将樣本檔案中的第二個<body>替換為</body>
編寫sed腳本為:
[jacob@localhost ~] #cat sed.sh
/body/{
s//\/body/2 注:替換與行比對相同的内容即body,替換為/body,但僅替換第二個body為/body。
執行sed程式的結果:
[jacob@localhost ~] #sed -f sed.sh test.txt
<body>Hello the World!</body>
---------------------------------------------------------------------------------------------
示例2所使用的樣本檔案為:
<body>
h1Helloh1
h2Helloh2
h3Helloh3
</body>
示例2:給所有第一個的h1,h2等添加<>;第二個h1,h2添加</>
/h[0-9]/{ 注:比對h緊跟一個數字的行
s//\<&\>/1 注:替換與上一行中比對内容相同的内容,即替換h[0-9],替換為<&>,其中&即前面要替換的内容。
s//\<\/&\>/2 注:上一條指令僅替換第一個h1,h2...,本行指令用來替換第二個h1,h2...。
<h1>Hello</h1>
<h2>Hello</h2>
<h3>Hello</h3>
技巧:關于 's///' 指令的另一個妙處是 '/' 分隔符有許多替換選項。
如果規則表達式或替換字元串中有許多斜杠,則可以通過在 's' 之後指定一個不同的字元來更改分隔符。
示例:$ sed -e 's:/usr/local:/usr:g' mylist.txt 這裡:為分隔符。
3.删除(d ,delete):
删除指令删除比對的行,而且删除指令還會改變sed腳本中指令的執行順序。因為:
比對的行一旦被删除,模式空間将變為“空”,自然不會再執行sed腳本後續的指令。
删除指令将導緻讀取新的輸入行(下一行),而sed腳本中的指令則從頭開始執行。
注意事項:删除将删除整個行,而不隻是删除比對的内容。(如要删除比對的内容,可以使用替換)
示例1:删除檔案中的空白行
/.*/{
/^$/d
4.追加(a):在比對行後追加内容
示例所使用的樣本檔案為:
示例程式:sed '/static/a IPADDR=192.168.0.1' test.txt 注:在static行後添加一行。
5.插入(i):在比對行前插入内容
示例程式:sed '/NETMASK/i IPADDR=192.168.0.1' test.txt 注:在NETMASK行前添加一行。
6.更改(c):更改比對行的内容
示例程式:sed '/ONBOOT/c ONBOOT=yes' test.txt 注:包含ONBOOT的行(整行)替換為ONBOOT=yes。
7.列印(l):顯示模式空間中的内容,顯示非列印字元,一般與-n一起使用,否則會輸出兩次。
示例程式:sed -n '1,2l' test.txt 注:如果使用sed腳本檔案,需要#n屏蔽自動輸出
結果如下:
DEVICE=eth0$
ONBOOT=yes$
8.轉換(y):按字元的轉換(Transform) [address]y/source-chars/dest-chars/
netmask=255.255.255.0
/netmask/y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/ 将小寫轉換為大寫
9.列印(p):作用類似與l(列印),但不顯示非顯示字元,一般與-n配合使用,腳本檔案中需要#n
sed -n '1,2p' test.txt 僅顯示第一、第二行的内容。
10.讀寫檔案(r,w):[line-address]r file, [address]w file
[jacob@localhost ~] #cat name.txt
Jacob
Tom
Jerry
[jacob@localhost ~] #cat mail.txt
$r mail.txt
[jacob@localhost ~] #sed -f sed.sh name.txt
11.退出(q): 比對位址後退出sed腳本
sed '10q' test.txt 列印檔案前10行内容(到第10行後退出sed腳本指令)。
12.下一步(n): [address]n 輸出模式空間中的内容,然後讀取輸入的下一行。
next指令改變了正常的sed腳本指令的流程,預設隻有到sed腳本的底部才會輸出模式空間的内容。
next指令導緻輸入的下一行取代模式空間中的目前行,sed腳本中的後續指令将應用于替換後的行,而不是目前行。
n 讀取下一行後删除(删除偶數行)。
/.*/d
13.Next(N):
多行Next(N)指令通過讀取新的輸入行,并将它添加到模式空間的現有内容之後,來建立多行模式空間。
模式空間的最初内容與新的輸入行之間用換行符分隔。在模式空間中插入的換行符可以使用\n比對。
示例1:
111
222
333
[jacob@localhost ~] #cat sed.sh
#n
/222/{
N
l
[jacob@localhost ~] #sed -f sed.sh test.txt 結果如下:
222\n222
示例2:
222\n222
222\n333
14.Print(P):多行列印(P)與列印(p)稍有不同,該指令僅輸出多行模式空間中的第一部分,直到第一個插入的\n換行符為止。
示例:
#cat test.txt #cat test.txt #cat test.txt #cat test.txt
111 111 111 111
222 222 222 222
333 333 333 333
444 444 444 444
555 555 555 555
666 666 666 666
-----------------------------------------------------------------------------------------------------
#cat sed.sh #cat sed.sh #cat sed.sh #cat sed.sh
/.*/{ /.*/{ /.*/{ /.*/{
N N N N
} l P p
} } }
------------------------------------------------------------------------------------------------------
結果: 結果: 結果: 結果:
111 111\n222$ 111 111
222 111 111 222
333 222 222 111
444 333\n444$ 333 222
555 333 333 333
666 444 444 444
555\n666$ 555 333
555 555 444
666 666 555
666
555
-------------------------------------------------------------------------------------------------------
流程說明:1.sed腳本檔案使用了N,但後續沒有任何指令,是以僅按sed自動輸出,即輸出源檔案所有内容。
2.sed腳本問使用了N,l表示顯示模式空間的内容,即111\n222(N的作用為把下一行追加至行尾),
同時sed腳本的自動輸出功能會把源檔案内容顯示出來即111,222。
依次類推,sed腳本繼續讀取第三行333,并使用N将444追加至行尾,使用l顯示模式空間的内容,
sed自動輸出再把源檔案内容顯示出來。
3.sed腳本檔案使用了N将下一行追加至行尾,現在模式空間中的内容為111\n222,而P指令的作用是
列印模式空間中的第一部分内容直到\n結尾,即僅列印111,這時sed的自動輸出功能把源檔案
内容列印出來111,222。依次類推讀取第三行333,N将444追加至行尾,P列印\n前的内容,
同時sed腳本自動将源問内容輸出。
4.原理類似與第三個sed腳本,但p列印時\n看作是回車換行,是以列印出來的是111回車222。
-----------------------------------------------------------------------------------------------------
15.Delete(D):
删除指令(d)删除模式空間中的内容并導緻讀入新的輸入行,而sed腳本重新執行。
删除指令(D)删除模式空間中直到第一個插入的換行符(\n)前的這部分内容,它不會讀入新的輸入行,并傳回sed腳本的頂端,
使得剩餘指令繼續應用于模式空間中剩餘的内容。
16.Hold(h,H),Get(g,G):
模式空間是存放目前輸入行的緩沖區。還有一個稱為保持空間(hold space)的緩沖區。
模式空間的内容可以複制到保持空間,保持空間的内容同樣可以複制到模式空間。
有一組指令用于在兩者之間移動資料。
Hold(h|H) 将模式空間的内容複制或追加到保持空間
Get(g|G) 将保持空間的内容複制或追加到模式空間
Exchange(x) 交換保持空間與模式空間的内容
示例:反轉部分行
1
2
11
22
/1/{
h
d
/2/{
G
結果:
17.branch,test
分支(b),測試(t)指令将sed腳本中的控制轉移到包含特殊标簽的行,如果沒有指定的标簽,則将控制轉移到腳本結尾處。
分支指令是無條件轉移,測試指令用于有條件轉移,測試隻有當替換指令改變目前行成功時才會執行。
标簽是任意不多于7個字元的序列,标簽本身占據一行并以冒号開始:
:mylable
冒号與标簽之間不允許有空格
當分支或測試指令中指定标簽時,在指令與标簽之間允許有空格:
b mylabel 注:不要在标簽後插入空格
五、綜合案例(案例摘自GNU sed官網)
綜合案例1:重命名檔案名為小寫
#! /bin/sh
# rename files to lower/upper case...
#
# usage:
# move-to-lower *
# move-to-upper *
# or
# move-to-lower -R .
# move-to-upper -R .
help()
{
cat << eof
Usage: $0 [-n] [-r] [-h] files...
-n do nothing, only see what would be done
-R recursive (use find)
-h this message
files files to remap to lower case
Examples:
$0 -n * (see if everything is ok, then...)
$0 *
$0 -R .
eof
}
apply_cmd='sh'
finder='echo "$@" | tr " " "\n"'
files_only=
while :
do
case "$1" in
-n) apply_cmd='cat' ;;
-R) finder='find "$@" -type f';;
-h) help ; exit 1 ;;
*) break ;;
esac
shift
done
if [ -z "$1" ]; then
echo Usage: $0 [-h] [-n] [-r] files...
exit 1
fi
LOWER='abcdefghijklmnopqrstuvwxyz'
UPPER='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
case `basename $0` in
*upper*) TO=$UPPER; FROM=$LOWER ;;
*) FROM=$UPPER; TO=$LOWER ;;
esac
eval $finder | sed -n '
# remove all trailing slashes
s/\/*$//
# add ./ if there is no path, only a filename
/\//! s/^/.\//
# save path+filename
h
# remove path
s/.*\///
# do conversion only on filename
y/'$FROM'/'$TO'/
# now line contains original path+file, while
# hold space contains the new filename
x
# add converted file name to line, which now contains
# path/file-name\nconverted-file-name
G
# check if converted file name is equal to original file name,
# if it is, do not print nothing
/^.*\/\(.*\)\n\1/b
# now, transform path/fromfile\n, into
# mv path/fromfile path/tofile and print it
s/^\(.*\/\)\(.*\)\n\(.*\)$/mv "\1\2" "\1\3"/p
' | $apply_cmd
綜合案例2:擷取bash環境變量
#!/bin/sh
set | sed -n '
:x
# if no occurrence of ‘=()’ print and load next line
/=()/! { p; b; }
/ () $/! { p; b; }
# possible start of functions section
# save the line in case this is a var like FOO="() "
# if the next line has a brace, we quit because
# nothing comes after functions
n
/^{/ q
# print the old line
x; p
# work on the new line now
x; bx
'
本文轉自丁丁曆險51CTO部落格,原文連結:http://blog.51cto.com/manual/908002 ,如需轉載請自行聯系原作者