天天看點

Shell腳本之sed篇

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

[email protected]

[email protected]

[email protected]

$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 ,如需轉載請自行聯系原作者