2014-03-31 47 views
2

這裏是文本的例子塊我想格式化:的sed的「N」命令工作間歇

<tr><td></td><td>tear a cat in, to make all split.</td><td></td></tr> 
<tr><td></td><td class="tdci">The raging rocks</td><td></td></tr> 
<tr><td></td><td class="tdci">The foolish Fates.</td></tr> 
<tr><td></td><td>This was lofty! Now name the rest of the players.</td><td></td></tr> 

使用這兩個「sed的」在腳本命令:

sed -ri '/^<tr><td><\/td><td>/N;s/(\n<tr><td><\/td><td class="tdci">)/\n<tr><td>\&nbsp;<\/td><\/tr>\1/' "$f" #insert table row with empty data fields (blank line) above first line with 'class="tdci"' 
sed -ri '/^<tr><td><\/td><td class="tdci">/N;s/(\n<tr><td><\/td><td>)/\n<tr><td>\&nbsp;<\/td><\/tr>\1/' "$f" #insert table row with empty data fields (blank line) after last line with 'class="tdci"' 

這裏是結果:

<tr><td></td><td>tear a cat in, to make all split.</td><td></td></tr> 
<tr><td>&nbsp;</td></tr> 
<tr><td></td><td class="tdci">The raging rocks</td><td></td></tr> 
<tr><td></td><td class="tdci">The foolish Fates.</td></tr> 
<tr><td></td><td>This was lofty! Now name the rest of the players.</td><td></td></tr> 

所以第sed命令作品通過將第一行無線上方的空白表格行th class="tdci",但幾乎相同的第二個sed命令意圖在最後一行後面插入空白表格行,class="tdci"不起作用。

我通常會保存這些類型的編輯,在多行之間進行編輯,因爲我從來沒有類似命令的問題,但由於某種原因,sed的「N;s/」一直是我碰到的,因爲在這例如,其中一個實例工作正常,但第二個實例不工作,在這些命令運行之前,該腳本刪除所有前導/尾隨空白和任何Winblowz回車(\r

由於我有大量要編輯的文件當然,如果有人能夠看到任何明顯的我做錯的事情,我當然更願意在腳本中得到這個工作。

其他細節:

對不起,我忘了提,我運行在Linux操作系統(Debian的穩定)

+4

有很多HTML解析器和漂亮的打印機;利用這些。 – devnull

+0

你在做什麼操作系統?什麼版本的sed? – Floris

+0

@devnull我欣賞HTML的建議,但讓我們保持專注。我的問題是關於特定'sed'命令的行爲;我試圖操作的文本可能很有可能是HTML以外的格式。 – nanker

回答

5

開始小sed!這裏有一個簡單的測試案例,你在做什麼:

a1 
b1 
b2 
a2 

這裏是你的代碼轉換爲這個測試的情況下,嘗試前的第一個「b」和c2在最後插入c1

sed -ri '/a/N; s/(\nb)/\nc1\1/' file 
sed -ri '/b/N; s/(\na)/\nc2\1/' file 

第一個命令,像你說的,似乎工作:

a1 
c1 
b1 
b2 
a1 

二不,只是給你同樣的結果上面而不是插入c2

這裏就是你可能想過會發生,不正確的部分以粗體顯示:

  1. a1被讀取並打印。
  2. c1被讀取和打印。
  3. b1被讀取。
    • 它匹配/b/,b2N一起閱讀。它與不匹配。
    • b1印刷
  4. b2被讀取的第二時間
    • 它匹配/b/,aN一起閱讀。
    • 它匹配\na。附加c2
    • b2\nc2\na被打印。

下面是實際發生的事情,

  1. a1被讀取並打印。
  2. c1被讀取和打印。
  3. b1被讀取。
    • 它匹配/b/,b2N一起閱讀。它與不匹配。
    • b1\nb2印刷
  4. a2被讀取並打印,因爲b2上面已經讀取。

這裏有一個工作命令:

sed -ri '/b/ { :b; N; s/\na/\nc2&/; te; P; D; bb; }; :e;' file 

僞代碼 - 在評論大致相當於SED的一部分 - 這就是:

if (input.matches("b")) {        // /b/ { 
    while(true) {           // :b 
    input += "\n" + readline();       // N 
    if(input.matches("\na")) {       // s/\na/ .. 
     input = input.replace("(\na)", "\nc2\1");   // .. \nc2&/ 
     goto exit;          // te 
    } 
    print(input.substring(0, input.indexOf('\n'));  // P 
    input = input.substring(input.indexOf('\n') + 1); // D 
    }              // bb 
}              // } 
:exit             // :e 

翻譯回您的數據:

sed -ri '/^<tr><td><\/td><td class="tdci">/ { :b; N; s/(\n<tr><td><\/td><td>)/\n<tr><td>\&nbsp;<\/td><\/tr>\1/; te; P; D; bb; }; :e' "$f" 
+0

@tog:哇!它的工作原理,但說實話,我迷路了,將不得不開始閱讀你詳細的'sed'選項,以幫助制定方式和原因的正面或反面。 – nanker

+1

+1;很好的分析;你的僞代碼缺少字符串替換命令;如果您直接將僞命令與註釋中的「sed」對應關聯起來,它會更有幫助。 – mklement0

+1

@ mklement0很好。固定 –

2

@that other guy's excellent answer顯示如何做w sed

然而,sed可以是腦折彎機,當涉及到像這些是自然界中有些程序上的問題,所以這裏是一個awk解決方案,可能更容易理解

awk -v blockRegex='^<tr><td><\/td><td class="tdci">' \ 
    -v lineToInsert='<tr><td>\&nbsp;<\/td><\/tr>' \ 
    ' 
    # Print a line BEFORE the FIRST line matching `blockRegex`. 
    $0 ~ blockRegex { if (!afterFirst) {print lineToInsert; afterFirst=inBlock=1} } 
    # Print a line AFTER the LAST (contiguous) line matching `blockRegex`. 
    inBlock && $0 !~ blockRegex { print lineToInsert; afterFirst=inBlock=0 } 
    # Print the input line. 
    { print } 
    ' \ 
    file 

注意,這可以進一步優化,但我想保持更簡單的澄清邏輯。

  • blockRegex傳遞在作爲變量(與選項-v)至之前和在這之後,線將被插入識別的連續行的塊 - 用線將被插入在作爲可變lineToInsert通過。
  • $0 ~ blockRegex匹配一行感興趣的行中的每一行,並打印要插入的行,如果它是行中的第一行行,如狀態變量afterFirst所示;狀態變量inBlock指示手邊的線在感興趣的塊內。
  • inBlock && $0 !~ blockRegex匹配第一個感興趣的塊並打印要插入的行,然後重置狀態變量。
  • print只是簡單地打印輸入行。

注意的是,使用的狀態變量的依賴於未初始化的變量中awk默認爲0(其在一個布爾上下文視爲false;類似地,一個非零值評估爲true)。