在這篇 sed 系列的總結性文章中,Daniel Robbins 帶您體驗 sed 的真正力量。在介" name="description" />

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • sed 實例,第 3 部分

    發表于:2007-07-04來源:作者:點擊數: 標簽:
    通用線程 -- sed 實例,第 3 部分 轉載自:IBM developerWorks 中國網站 java script:window.open(this.src);" style="CURSOR: pointer" onload="return imgzoom(this,550)"> 在這篇 sed 系列的總結性文章中,Daniel Robbins 帶您體驗 sed 的真正力量。在介
    通用線程 -- sed 實例,第 3 部分
           
        轉載自:IBM developerWorks 中國網站

    javascript:window.open(this.src);" style="CURSOR: pointer" onload="return imgzoom(this,550)">
    在這篇 sed 系列的總結性文章中,Daniel Robbins 帶您體驗 sed 的真正力量。在介紹完幾個重要的 sed 腳本之后,他將通過將一個 Quicken .QIF 文件轉換成可讀文本格式來演示一些基本 sed 腳本的編寫。該轉換腳本不僅實用,而且還是展現 sed 腳本編寫能力的極佳示例。

    強健的 sed
    第二篇 sed 文章中,我提供了一些示例來演示 sed 的工作原理,但是它們當中很少有示例能實際做特別有用的事。在這篇 sed 系列的最后文章中,我要改變那種方式,并使用 sed 來做實際的事。我將為您顯示幾個示例,它們不僅演示 sed 的能力,而且還做一些真正巧妙(和方便)的事。例如,在本文的后半部,將為您演示如何設計一個 sed 腳本來將 .QIF 文件從 Intuit 的 Quicken 金融程序轉換成具有良好格式的文本文件。在那樣做之前,我們將看一下不怎么復雜但卻很有用的 sed 腳本。

    文本轉換
    第一個實際腳本將 UNIX 風格的文本轉換成 DOS/Windows 格式。您可能知道,基于 DOS/Windows 的文本文件在每一行末尾有一個 CR(回車)和 LF(換行),而 UNIX 文本只有一個換行。有時可能需要將某些 UNIX 文本移至 Windows 系統,該腳本將為您執行必需的格式轉換。

     $ sed -e 's/$/r/' myunix.txt > mydos.txt 

    在該腳本中,'$' 規則表達式將與行的末尾匹配,而 'r' 告訴 sed 在其之前插入一個回車。在換行之前插入回車,立即,每一行就以 CR/LF 結束。請注意,僅當使用 GNU sed 3.02.80 或以后的版本時,才會用 CR 替換 'r'。如果還沒有安裝 GNU sed 3.02.80,請在我的第一篇 sed 文章中查看如何這樣做的說明。

    我已記不清有多少次在下載一些示例腳本或 C 代碼之后,卻發現它是 DOS/Windows 格式。雖然很多程序不在乎 DOS/Windows 格式的 CR/LF 文本文件,但是有幾個程序卻在乎 -- 最著名的是 bash,只要一遇到回車,它就會出問題。以下 sed 調用將把 DOS/Windows 格式的文本轉換成可信賴的 UNIX 格式:

     $ sed -e 's/.$//' mydos.txt > myunix.txt 

    該腳本的工作原理很簡單:替代規則表達式與一行的最末字符匹配,而該字符恰好就是回車。我們用空字符替換它,從而將其從輸出中徹底刪除。如果使用該腳本并注意到已經刪除了輸出中每行的最末字符,那么,您就指定了已經是 UNIX 格式的文本文件。也就沒必要那樣做了!

    反轉行
    下面是另一個方便的小腳本。與大多數 Linux 發行版中包括的 "tac" 命令一樣,該腳本將反轉文件中行的次序。"tac" 這個名稱可能會給人以誤導,因為 "tac" 不反轉行中字符的位置(左和右),而是反轉文件中行的位置(上和下)。用 "tac" 處理以下文件:

     foo bar oni 

    ....將產生以下輸出:

     oni bar foo 

    可以用以下 sed 腳本達到相同目的:

     $ sed -e '1!G;h;$!d' forward.txt > backward.txt 

    如果登錄到恰巧沒有 "tac" 命令的 FreeBSD 系統,將發現該 sed 腳本很有用。雖然方便,但最好還是知道該腳本為什么那樣做。讓我們對它進行討論。

    反轉解釋
    首先,該腳本包含三個由分號隔開的單獨 sed 命令:'1!G'、'h' 和 '$!d'?,F在,需要好好理解用于第一個和第三個命令的地址。如果第一個命令是 '1G',則 'G' 命令將只應用第一行。然而,還有一個 '!' 字符 -- 該 '!' 字符忽略該地址,即,'G' 命令將應用到除第一行之外的所有行。'$!d' 命令與之類似。如果命令是 '$d',則將只把 'd' 命令應用到文件中的最后一行('$' 地址是指定最后一行的簡單方式)。然而,有了 '!' 之后,'$!d' 將把 'd' 命令應用到除最后一行之外的所有行?,F在,我們所要理解的是這些命令本身做什么。

    當對上面的文本文件執行反轉腳本時,首先執行的命令是 'h'。該命令告訴 sed 將模式空間(保存正在處理的當前行的緩沖區)的內容復制到保留空間(臨時緩沖區)。然后,執行 'd' 命令,該命令從模式空間中刪除 "foo",以便在對這一行執行完所有命令之后不打印它。

    現在,第二行。在將 "bar" 讀入模式空間之后,執行 'G' 命令,該命令將保留空間的內容 ("foon") 附加到模式空間 ("barn"),使模式空間的內容為 "barnfoon"。'h' 命令將該內容放回保留空間保護起來,然后,'d' 從模式空間刪除該行,以便不打印它。

    對于最后的 "oni" 行,除了不刪除模式空間的內容(由于 'd' 之前的 '$!')以及將模式空間的內容(三行)打印到標準輸出之外,重復同樣的步驟。

    現在,要用 sed 執行一些強大的數據轉換。

    sed QIF 魔法
    過去幾個星期,我一直想買一份

    Quicken 來結算我的銀行帳戶。Quicken 是一個非常好的金融程序,當然會成功地完成這項工作。但是,經過考慮之后,我覺得自己可以輕易編寫某個軟件來結算我的支票簿。我想,畢竟,我是個軟件開發人員!

    我開發了一個很好的小型支票簿結算程序(使用 awk),它通過分析包含我的所有交易的文本文件的語法來計算余額。略微調整之后,我將其改進,以便可以象 Quicken 那樣跟蹤不同的貸款和借款類別。但是,我還要添加一個特性。最近,我將帳戶轉移到一家有聯機 Web 帳戶界面的銀行。有一天,我注意到,這家銀行的 Web 站點允許以 Quicken 的 .QIF 格式下載我的帳戶信息。我馬上覺得,如果可以將該信息轉換成文本格式,那就太棒了。

    兩種格式的故事
    在查看 QIF 格式之前,先看一下我的 checkbook.txt 格式:

     28 Aug 2000     food    -       -       Y     Supermarket             30.94 25 Aug 2000     watr    -       103     Y     Check 103               52.86 

    在我的文件中,所有字段都由一個或多個制表符分開,每個交易占據一行。日期之后的下一個字段列出支出類型(如果是收入項,則為 "-")。第三個字段列出收入類型(如果是支出項,則為 "-")。然后,是一個支票號字段(如果為空,則還是 "-"),一個交易完成字段("Y" 或 "N"),一個注釋和一個美元金額字段?,F在,讓我們看一下 QIF 格式。當用文本查看器查看下載的 QIF 文件時,它看起來如下:

     !Type:Bank D08/28/2000 T-8.15 N PCHECKCARD SUPERMARKET ^ D08/28/2000 T-8.25 N PCHECKCARD PUNJAB RESTAURANT ^ D08/28/2000 T-17.17 N PCHECKCARD SUPERMARKET 

    瀏覽過文件之后,不難猜出其格式 -- 忽略第一行,其余的格式如下:

     D<數據>  
    T<交易量>
    N<支票號>
    P<描述>
    ^ (這是字段分隔符)

    開始處理
    在處理象這樣重要的 sed 項目時,不要氣餒 -- sed 允許您將數據逐漸修改成最終形式。在進行當中,可以繼續細化 sed 腳本,直到輸出與預期的完全一樣為止。無需在試第一次時就保證其完全正確。

    要開始,首先創建一個名為 "qiftrans.sed" 的文件,然后開始修改數據:

     1d /^^/d s/[[:cntrl:]]//g 

    第一個 '1d' 命令刪除第一行,第二個命令從輸出除去那些討厭的 '^' 字符。最后一行除去文件中可能存在的任何控制字符。既然在處理外來文件格式,我想消除在中途遇到任何控制字符的風險。到目前為止,一切順利?,F在,要向該基本腳本中添加一些處理功能:

     1d /^^/d s/[[:cntrl:]]//g /^D/ { 
    s/^D(.*)/1tOUTYtINNYt/
    s/^01/Jan/ s/^02/Feb/
    s/^03/Mar/ s/^04/Apr/
    s/^05/May/ s/^06/Jun/
    s/^07/Jul/ s/^08/Aug/
    s/^09/Sep/ s/^10/Oct/
    s/^11/Nov/ s/^12/Dec/
    s:^(.*)/(.*)/(.*):2 1 3: }

    首先,添加一個 '/^D/' 地址,以便 sed 只在遇到 QIF 數據字段的第一個字符 'D' 時才開始處理。當 sed 將這樣一行讀入其模式空間時,將按順序執行花括號中的所有命令。

    花括號中的第一個命令將把如下行:

     D08/28/2000 

    變換成:

     08/28/2000OUTYINNY 

    當然,現在的格式還不完美,但沒關系。我們將在進行過程中逐漸細化模式空間的內容。后面 12 行的最后效果是將數據變換成三個字母的格式,最后一行從數據中除去三個斜杠。最后得到這一行:

     Aug 28 2000OUTYINNY 

    OUTY 和 INNY 字段是占位符,以后將被替換?,F在還不能確定它們,因為如果美元金額為負,將把 OUTY 和 INNY 設置成 "misc" 和 "-",但是,如果美元金額為正,將分別把它們更改成 "-" 和 "inco"。既然還沒有讀入美元金額,所以,需要暫時使用占位符。

    細化
    現在進一步細化:

     1d  /^^/d s/[[:cntrl:]]//g  /^D/ {
    s/^D(.*)/1tOUTYtINNYt/
    s/^01/Jan/ s/^02/Feb/
    s/^03/Mar/ s/^04/Apr/
    s/^05/May/ s/^06/Jun/
    s/^07/Jul/ s/^08/Aug/
    s/^09/Sep/ s/^10/Oct/
    s/^11/Nov/ s/^12/Dec/
    s:^(.*)/(.*)/(.*):2 1 3:
    N N N
    s/nT(.*)nN(.*)nP(.*)/NUM2NUMttYtt3tAMT1AMT/
    s/NUMNUM/-/ s/NUM([0-9]*)NUM/1/
    s/([0-9]),/1/ }

    后七行有些復雜,所以將詳細討論它們。首先,連續使用三個 'N' 命令。'N' 命令告訴 sed 將下一行讀入輸入中,然后將其附加到當前模式空間。這三個 'N' 命令導致將下三行附加到當前模式空間緩沖區,現在這一行看起來如下:

     28 Aug 2000OUTYINNYnT-8.15nNnPCHECKCARD SUPERMARKET 

    sed 的模式空間變得很難看 -- 需要除去額外的新行,并執行某些附加的格式化。要這樣做,將使用替代命令。要匹配的模式為:

     'nT.*nN.*nP.*' 

    這將與后面依次跟有 'T'、零或多個字符、新行、'N'、任何數量的字符、新行、'P'、以及任何數量字符的新行匹配。呀!這個規則表達式將與剛剛附加到模式空間的三行的全部內容匹配。但我們要重新格式化該區域,而不是整個替換它。美元金額、支票號(如果有的話)和描述需要出現在替換字符串中。要這樣做,我們用帶有反斜杠的圓括號括起那些“感興趣部分”,以便可以在替換字符串中引用它們(使用 '1'、'2 和 '3' 來告訴 sed 將它們插入到何處)。以下是最后的命令:

     s/nT(.*)nN(.*)nP(.*)/NUM2NUMttYtt3tAMT1AMT/  

    該命令將我們的行變換成:

      28 Aug 2000  OUTY  INNY  NUMNUM    Y   CHECKCARD SUPERMARKET AMT-8.15AMT 

    雖然該行正變得好一些,但是,有幾件事一看就有點...啊...有趣。首先是那個愚蠢的 "NUMNUM" 字符串 -- 其目的何在?如果查看 sed 腳本的后兩行,就會發現其目的,后兩行將把 "NUMNUM" 替換成 "-",而把 "NUM"<number>"NUM" 替換成 <number>。如您所見,用愚蠢的標記括起支票號允許我們在該字段為空時方便地插入一個 "-"。

    結束嘗試
    最后一行除去數字后的逗號。它把如 "3,231.00" 這樣的美元金額轉換成我使用的格式 "3231.00"?,F在,讓我們看一下最終腳本:

    最終的“QIF 到文本”腳本
     1d /^^/d s/[[:cntrl:]]//g /^D/ { s/^D(.*)/1tOUTYtINNYt/ 
    s/^01/Jan/ s/^02/Feb/ s/^03/Mar/ s/^04/Apr/ s/^05/May/
    s/^06/Jun/ s/^07/Jul/ s/^08/Aug/ s/^09/Sep/ s/^10/Oct/
    s/^11/Nov/ s/^12/Dec/ s:^(.*)/(.*)/(.*):2 1 3:
    N N N s/nT(.*)nN(.*)nP(.*)/NUM2NUMttYtt3tAMT1AMT/
    s/NUMNUM/-/ s/NUM([0-9]*)NUM/1/ s/([0-9]),/1/
    /AMT-[0-9]*.[0-9]*AMT/b fixnegs
    s/AMT(.*)AMT/1/ s/OUTY/-/ s/INNY/inco/
    b done :fixnegs s/AMT-(.*)AMT/1/ s/OUTY/misc/
    s/INNY/-/ :done }

    附加的十一行使用替代和一些分支功能來美化輸出。首先看一下這行:

             /AMT-[0-9]*.[0-9]*AMT/b fixnegs  

    該行包含一個格式為 "/regexp/b label" 的分支命令。如果模式空間與規則表達式匹配,sed 將分支到 fixnegs 標號。您應該可以輕易找到該標號,它在代碼中為 ":fixnegs"。如果規則表達式不匹配,則以常規方式繼續處理下一個命令。

    既然您理解該命令本身的工作原理,讓我們看一下分支。如果看一下分支規則表達式,將看到它與后面依次跟有 '-'、任意數量的數字、一個 '.'、任意數量的數字和 'AMT' 的字符串 'AMT' 匹配。就象我確信您已猜到一樣,該規則表達式專門處理負的美元金額。在這之前,用 'ATM' 括起美元金額,以便以后可以輕易找到它。因為規則表達式只與以 '-' 開始的美元金額匹配,所以,該分支只在恰巧處理借款時才發生。如果正處理貸款,應該將 OUTY 設置成 'misc',將 INNY 設置成 '-',并且應該除去貸款數量前面的負號。如果跟蹤代碼的流程,將看到實際情況正是這樣。如果不執行分支,則用 '-' 替換 OUTY,用 'inco' 替換 INNY。完成了!現在輸出行是完美的:

     28 Aug 2000misc--       Y     CHECKCARD SUPERMARKET  -8.15 

    別犯糊涂
    如您所見,只要循序漸進地解決問題,使用 sed 轉換數據就沒有那么難。不要試圖使用一個 sed 命令或一下子解決所有問題。相反,要朝著目標逐步進行,并不斷改進 sed 腳本,直到其輸出正如您希望那樣為止。sed 有許多功能,希望您已非常熟悉其內部工作原理并繼續努力以進一步掌握它!

    參考資料

    • 閱讀 developerWorks 上 Daniel 的其它 sed 文章:通用線程:sed 實例,第 1 部分第 2 部分。
    • 查看 Eric Pement 極佳的 sed faq。
    • 可以在 ftp.gnu.org 找到 sed 3.02 資源。
    • 將在 alpha.gnu.org 找到很好的新的 sed 3.02.80。
    • 另外,Eric Pement 還有一些方便的 sed 單行程序,任何有抱負的 sed 高手都應該看一下。
    • 如果想看好的老式書籍,O'Reilly 的 sed & awk, 2nd Edition 將是極佳選擇。
    • 可能想閱讀 7th edition UNIX's sed man page(大概 1978?。?。
    • 閱讀 Felix von Leitner 短小的 sed tutorial。
    • using regular expressions 中復習,發現和修改這個免費 dW 獨家教程文本中的模式。

    關于作者
    Daniel Robbins 居住在新墨西哥州的 Albuquerque。他是 Gentoo Technologies, Inc. 的總裁兼 CEO,Gentoo Linux(用于 PC 的高級 Linux)和 Portage 系統(Linux 的下一代端口系統)的創始人。他還是 Macmillan 書籍 Caldera OpenLinux Unleashed、SuSE Linux UnleashedSamba Unleashed 的作者。Daniel 自小學二年級起就與計算機結下不解之緣,那時他首先接觸的是 Logo 程序語言,并沉溺于 Pac-Man 游戲中。這也許就是他至今仍擔任 SONY Electronic Publishing/Psygnosis 的首席圖形設計師的原因所在。Daniel 喜歡與妻子 Mary 和新出生的女兒 Hadassah 一起共度時光??赏ㄟ^ drobbins@gentoo.org 與 Daniel Robbins 聯系。


    by

    原文轉自:http://www.kjueaiud.com

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>