• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            為生存而奔跑

               :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
              271 Posts :: 0 Stories :: 58 Comments :: 0 Trackbacks

            留言簿(5)

            我參與的團(tuán)隊(duì)

            搜索

            •  

            積分與排名

            • 積分 - 328427
            • 排名 - 74

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            強(qiáng)健的 sed

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





            回頁(yè)首


            文本轉(zhuǎn)換

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

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

            在 該腳本中,'$' 規(guī)則表達(dá)式將與行的末尾匹配,而 '\r' 告訴 sed 在其之前插入一個(gè)回車(chē)。在換行之前插入回車(chē),立即,每一行就以 CR/LF 結(jié)束。請(qǐng)注意,僅當(dāng)使用 GNU sed 3.02.80 或以后的版本時(shí),才會(huì)用 CR 替換 '\r'。如果還沒(méi)有安裝 GNU sed 3.02.80,請(qǐng)?jiān)谖业?第一篇 sed 文章中查看如何這樣做的說(shuō)明。

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

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

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





            回頁(yè)首


            反轉(zhuǎn)行

            下 面是另一個(gè)方便的小腳本。與大多數(shù) Linux 發(fā)行版中包括的 "tac" 命令一樣,該腳本將反轉(zhuǎn)文件中行的次序。"tac" 這個(gè)名稱(chēng)可能會(huì)給人以誤導(dǎo),因?yàn)?"tac" 不反轉(zhuǎn)行中字符的位置(左和右),而是反轉(zhuǎn)文件中行的位置(上和下)。用 "tac" 處理以下文件:

             foo bar oni 

            ....將產(chǎn)生以下輸出:

             oni bar foo 

            可以用以下 sed 腳本達(dá)到相同目的:

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

            如果登錄到恰巧沒(méi)有 "tac" 命令的 FreeBSD 系統(tǒng),將發(fā)現(xiàn)該 sed 腳本很有用。雖然方便,但最好還是知道該腳本為什么那樣做。讓我們對(duì)它進(jìn)行討論。





            回頁(yè)首


            反轉(zhuǎn)解釋

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

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

            現(xiàn)在,第二行。在將 "bar" 讀入模式空間之后,執(zhí)行 'G' 命令,該命令將保留空間的內(nèi)容 ("foo\n") 附加到模式空間 ("bar\n"),使模式空間的內(nèi)容為 "bar\n\foo\n"。'h' 命令將該內(nèi)容放回保留空間保護(hù)起來(lái),然后,'d' 從模式空間刪除該行,以便不打印它。

            對(duì)于最后的 "oni" 行,除了不刪除模式空間的內(nèi)容(由于 'd' 之前的 '$!')以及將模式空間的內(nèi)容(三行)打印到標(biāo)準(zhǔn)輸出之外,重復(fù)同樣的步驟。

            現(xiàn)在,要用 sed 執(zhí)行一些強(qiáng)大的數(shù)據(jù)轉(zhuǎn)換。





            回頁(yè)首


            sed QIF 魔法

            過(guò)去幾個(gè)星期,我一直想買(mǎi)一份 Quicken來(lái)結(jié)算我的銀行帳戶(hù)。Quicken 是一個(gè)非常好的金融程序,當(dāng)然會(huì)成功地完成這項(xiàng)工作。但是,經(jīng)過(guò)考慮之后,我覺(jué)得自己可以輕易編寫(xiě)某個(gè)軟件來(lái)結(jié)算我的支票簿。我想,畢竟,我是個(gè)軟件開(kāi)發(fā)人員!

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





            回頁(yè)首


            兩種格式的故事

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

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

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

             !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 

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

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





            回頁(yè)首


            開(kāi)始處理

            在處理象這樣重要的 sed 項(xiàng)目時(shí),不要?dú)怵H -- sed 允許您將數(shù)據(jù)逐漸修改成最終形式。在進(jìn)行當(dāng)中,可以繼續(xù)細(xì)化 sed 腳本,直到輸出與預(yù)期的完全一樣為止。無(wú)需在試第一次時(shí)就保證其完全正確。

            要開(kāi)始,首先創(chuàng)建一個(gè)名為 "qiftrans.sed" 的文件,然后開(kāi)始修改數(shù)據(jù):

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

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

             1d /^^/d s/[[:cntrl:]]//g /^D/ { 
            s/^D\(.*\)/\1\tOUTY\tINNY\t/
            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: }

            首先,添加一個(gè) '/^D/' 地址,以便 sed 只在遇到 QIF 數(shù)據(jù)字段的第一個(gè)字符 'D' 時(shí)才開(kāi)始處理。當(dāng) sed 將這樣一行讀入其模式空間時(shí),將按順序執(zhí)行花括號(hào)中的所有命令。

            花括號(hào)中的第一個(gè)命令將把如下行:

             D08/28/2000 

            變換成:

             08/28/2000	OUTY	INNY 

            當(dāng)然,現(xiàn)在的格式還不完美,但沒(méi)關(guān)系。我們將在進(jìn)行過(guò)程中逐漸細(xì)化模式空間的內(nèi)容。后面 12 行的最后效果是將數(shù)據(jù)變換成三個(gè)字母的格式,最后一行從數(shù)據(jù)中除去三個(gè)斜杠。最后得到這一行:

             Aug 28 2000	OUTY	INNY 

            OUTY 和 INNY 字段是占位符,以后將被替換。現(xiàn)在還不能確定它們,因?yàn)槿绻涝痤~為負(fù),將把 OUTY 和 INNY 設(shè)置成 "misc" 和 "-",但是,如果美元金額為正,將分別把它們更改成 "-" 和 "inco"。既然還沒(méi)有讀入美元金額,所以,需要暫時(shí)使用占位符。





            回頁(yè)首


            細(xì)化

            現(xiàn)在進(jìn)一步細(xì)化:

             1d  /^^/d s/[[:cntrl:]]//g  /^D/ {
            s/^D\(.*\)/\1\tOUTY\tINNY\t/
            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\(.*\)/NUM\2NUM\t\tY\t\t\3\tAMT\1AMT/
            s/NUMNUM/-/ s/NUM\([0-9]*\)NUM/\1/
            s/\([0-9]\),/\1/ }

            后七行有些復(fù)雜,所以將詳細(xì)討論它們。首先,連續(xù)使用三個(gè) 'N' 命令。'N' 命令告訴 sed 將 下一行讀入輸入中,然后將其附加到當(dāng)前模式空間。這三個(gè) 'N' 命令導(dǎo)致將下三行附加到當(dāng)前模式空間緩沖區(qū),現(xiàn)在這一行看起來(lái)如下:

             28 Aug 2000	OUTY	INNY	\nT-8.15\nN\nPCHECKCARD SUPERMARKET 

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

             '\nT.*\nN.*\nP.*' 

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

             s/\nT\(.*\)\nN\(.*\)\nP\(.*\)/NUM\2NUM\t\tY\t\t\3\tAMT\1AMT/  

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

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

            雖 然該行正變得好一些,但是,有幾件事一看就有點(diǎn)...啊...有趣。首先是那個(gè)愚蠢的 "NUMNUM" 字符串 -- 其目的何在?如果查看 sed 腳本的后兩行,就會(huì)發(fā)現(xiàn)其目的,后兩行將把 "NUMNUM" 替換成 "-",而把 "NUM"<number>"NUM" 替換成 <number>。如您所見(jiàn),用愚蠢的標(biāo)記括起支票號(hào)允許我們?cè)谠撟侄螢榭諘r(shí)方便地插入一個(gè) "-"。





            回頁(yè)首


            結(jié)束嘗試

            最后一行除去數(shù)字后的逗號(hào)。它把如 "3,231.00" 這樣的美元金額轉(zhuǎn)換成我使用的格式 "3231.00"。現(xiàn)在,讓我們看一下最終腳本:

            最終的“QIF 到文本”腳本

             1d /^^/d s/[[:cntrl:]]//g /^D/ { 	s/^D\(.*\)/\1\tOUTY\tINNY\t/ 
            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\(.*\)/NUM\2NUM\t\tY\t\t\3\tAMT\1AMT/
            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 }

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

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

            該行包含一個(gè)格式為 "/regexp/b label" 的分支命令。如果模式空間與規(guī)則表達(dá)式匹配,sed 將分支到 fixnegs 標(biāo)號(hào)。您應(yīng)該可以輕易找到該標(biāo)號(hào),它在代碼中為 ":fixnegs"。如果規(guī)則表達(dá)式不匹配,則以常規(guī)方式繼續(xù)處理下一個(gè)命令。

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

             28 Aug 2000	misc	-	-       Y     CHECKCARD SUPERMARKET  -8.15 





            回頁(yè)首


            別犯糊涂

            如 您所見(jiàn),只要循序漸進(jìn)地解決問(wèn)題,使用 sed 轉(zhuǎn)換數(shù)據(jù)就沒(méi)有那么難。不要試圖使用一個(gè) sed 命令或一下子解決所有問(wèn)題。相反,要朝著目標(biāo)逐步進(jìn)行,并不斷改進(jìn) sed 腳本,直到其輸出正如您希望那樣為止。sed 有許多功能,希望您已非常熟悉其內(nèi)部工作原理并繼續(xù)努力以進(jìn)一步掌握它!

            posted on 2010-05-20 21:35 baby-fly 閱讀(123) 評(píng)論(0)  編輯 收藏 引用 所屬分類(lèi): Ubuntu&Linux
            久久久久无码国产精品不卡| 日本欧美久久久久免费播放网| 国产成人精品综合久久久久| 久久九九免费高清视频| 国产成人无码久久久精品一| 亚洲午夜无码久久久久| 久久亚洲AV无码精品色午夜麻豆| 久久午夜福利电影| 久久久久亚洲AV成人网人人软件| 国产精品gz久久久| 久久无码国产| 久久香综合精品久久伊人| 色狠狠久久综合网| 亚洲AV日韩精品久久久久| 久久精品国产乱子伦| 久久99精品久久久久久久久久| 久久久久久亚洲精品成人| 青青青青久久精品国产h| 精品久久人人妻人人做精品| 亚洲国产精品综合久久一线| 亚洲va久久久噜噜噜久久天堂| 无码人妻久久一区二区三区| …久久精品99久久香蕉国产| 99久久夜色精品国产网站| 亚洲国产精品成人AV无码久久综合影院| 久久这里只有精品视频99| 国内精品伊人久久久久777| 91精品国产乱码久久久久久| 亚洲午夜久久影院| 久久天天躁狠狠躁夜夜躁2014| 乱亲女H秽乱长久久久| 久久精品国产亚洲5555| 亚洲人成网亚洲欧洲无码久久| 久久综合久久综合久久综合| 久久强奷乱码老熟女网站| 久久久久99精品成人片欧美 | 国产精品va久久久久久久| 香蕉久久影院| 久久久综合九色合综国产| 综合人妻久久一区二区精品| 精品国产热久久久福利|