2013-08-20 16 views
4

假設你有一些文字是這樣的:桑達:複雜的圖案更換後(在同一行)

foobar 42     | ff 00 00 00 00 
    foobaz 00     | 0a 00 0b 00 00 
    foobie 00     | 00 00 00 00 00 
    bar 00     | ab ba 00 cd 00 

,並要更改所有非00|的右側與包裹(),但僅限於|的LHS具有00。期望的結果:

foobar 42     | ff 00 00 00 00 
    foobaz 00     | (0a) 00 (0b) 00 00 
    foobie 00     | 00 00 00 00 00 
    bar 00     | (ab) (ba) 00 (cd) 00 

有沒有的要對此使用SED的好辦法,還是我試圖延伸超越語言的能力?

這裏是我的工作至今:

s/[^0]\{2\}/(&)/g包裝你的RHS值

/[^|]*00[^|]*|/可以作爲一個地址的命令只能在有效行

的訣竅,現在是制定一個操作命令在模式空間的一部分中執行。

這實際上不是面向行的,這可以解釋爲什麼我無法獲得可用的表達式。

+2

每當你發現自己在使用sed和包含單詞「space」(例如模式空間,保持空格)的發音短語時,就會使用錯誤的工具。 sed是簡單替換一行的優秀工具,但對於任何涉及構造函數s,g和p(帶-n)的任何東西,都應該使用awk。所有其他你可以用sed做的事情在20世紀70年代當awk被髮明並且應該被刪除的時候已經過時了。 –

回答

5
$ awk 'BEGIN{ FS=OFS="|" } $1~/ 00 /{gsub(/[^ ][^0 ]|[^0 ][^ ]/,"(&)",$2)} 1' file 
    foobar 42     | ff 00 00 00 00 
    foobaz 00     | (0a) 00 (0b) 00 00 
    foobie 00     | 00 00 00 00 00 
    bar 00     | (ab) (ba) 00 (cd) 00 

如果你想搜索有史以來字符串變得比2 0比較複雜,這裏有一個更普遍的可擴展的方法,因爲它不要求你寫否定字符串的RE:

$ awk ' 
    BEGIN{ FS=OFS="|" } 
    $1 ~/00 /{ 
     split($2,a,/ /) 
     $2="" 
     for (i=2;i in a;i++) 
      $2 = $2 " " (a[i] == "00" ? a[i] : "(" a[i] ")") 
    } 
    1 
' file 
    foobar 42     | ff 00 00 00 00 
    foobaz 00     | (0a) 00 (0b) 00 00 
    foobie 00     | 00 00 00 00 00 
    bar 00     | (ab) (ba) 00 (cd) 00 
+1

太棒了。我有一種感覺,這更多的是一個awk'ish問題:)。奇怪的是,如果沒有好的方式說「不是00.否則,一個非常乾淨的解決方案。 – Balthamos

+0

是的,可悲的是沒有運營商否定字符串。我更新我的答案,以顯示一個不同的解決方案,以避免這個問題 –

1

好吧試試這個!

$ sed '/00 *|/ { h; s/|.*/|/; x; s/.*|//; s/\(0[1-9a-f]\|[1-9a-f][0-9a-f]\)/(\1)/g; H; x; s/\n//; }' yourfile.txt 

輸出我得到的是這樣的:

foobar 42     | ff 00 00 00 00 
foobaz 00     | (0a) 00 (0b) 00 00 
foobie 00     | 00 00 00 00 00 
bar 00     | (ab) (ba) 00 (cd) 00 

編輯,所以它不會在|之前沒有00碰線。

+0

第一行的'ff'不應該被觸及......並且你不應該需要3次調用'sed'來完成這項工作。 –

+0

@JonathanLeffler ok,刪除了sed的3個調用,現在它使用保持緩衝區。 –

1

我覺得awk可能是這個工作的更好的工具,但它可以與sed做到:

sed '/^[^ ]* *00 *|/{ 
     :a 
     s/\(|.*[^(]\)\([0-9a-f][1-9a-f]\)/\1(\2)/ 
     t a 
     :b 
     s/\(|.*[^(]\)\([1-9a-f][0-9a-f]\)/\1(\2)/ 
     t b 
    }' data 

腳本查找線路CON在管道之前包含00,並且僅對這些線路應用操作。有兩種替代操作,每種都包含在一個循環中。 :a:b行是標籤。如果自上次跳轉後執行了替換操作,則t at b命令是對​​指定標籤的條件跳轉。這兩個替代幾乎是對稱的;第一個處理任何不以0結尾的數字;第二個處理任何不以0開始的數字;在他們之間,他們忽略了00。這些模式查找管道,任何字符序列都不以一個開括號(以及適當的一對數字結尾;它會取代它,以便數字在括號內結束。循環是必要的,因爲g修飾符不會從頭再次開始,並且模式向後遍歷數字。

鑑於此數據文件(一個稍微延長你的版本):

foobar 42     | ff 00 00 00 00 
foobaz 00     | 0a 00 0b 00 00 
foobie 00     | 00 00 00 00 00 
bar 00     | ab ba 00 cd 00 
fizbie 00    | ab ba 00 cd 90 
fizzbuzz 00    | ab ba 00 cd 09 

從腳本的輸出是:

foobar 42     | ff 00 00 00 00 
foobaz 00     | (0a) 00 (0b) 00 00 
foobie 00     | 00 00 00 00 00 
bar 00     | (ab) (ba) 00 (cd) 00 
fizbie 00    | (ab) (ba) 00 (cd) (90) 
fizzbuzz 00    | (ab) (ba) 00 (cd) (09) 

這是適度教育每個後添加p替代命令,因此您可以看到替換如何工作:

foobar 42     | ff 00 00 00 00 
foobaz 00     | 0a 00 (0b) 00 00 
foobaz 00     | (0a) 00 (0b) 00 00 
foobaz 00     | (0a) 00 (0b) 00 00 
foobie 00     | 00 00 00 00 00 
bar 00     | ab ba 00 (cd) 00 
bar 00     | ab (ba) 00 (cd) 00 
bar 00     | (ab) (ba) 00 (cd) 00 
bar 00     | (ab) (ba) 00 (cd) 00 
fizbie 00    | ab ba 00 (cd) 90 
fizbie 00    | ab (ba) 00 (cd) 90 
fizbie 00    | (ab) (ba) 00 (cd) 90 
fizbie 00    | (ab) (ba) 00 (cd) (90) 
fizbie 00    | (ab) (ba) 00 (cd) (90) 
fizzbuzz 00    | ab ba 00 cd (09) 
fizzbuzz 00    | ab ba 00 (cd) (09) 
fizzbuzz 00    | ab (ba) 00 (cd) (09) 
fizzbuzz 00    | (ab) (ba) 00 (cd) (09) 
fizzbuzz 00    | (ab) (ba) 00 (cd) (09) 
+0

啊哈,那個循環結構是謎題的一個缺失部分!需要記住的好消息我知道sed確實可以完成任務,儘管有點圓潤。 – Balthamos

+0

是的,awk解決方案更加整潔,儘管我懷疑還有其他的方法可以做'awk'解決方案,但是它們都比'sed'更加整潔,但是循環結構很強大,事實上,我從某個地方(從SO上的鏈接) 'sed'是圖靈完成](http://www.catonmat.net/blog/proof-that-sed-is-turing-complete/) - 但它不是一個通用語言的好選擇。 –

+0

雖然兩個來自輸入的前導空格被刪除;) – Dru

2

井看來,(雖然我做這一切的時候),該管道sed將sed將SED手段真的不是我在第一時間:這裏有一個

sed -r '/00.*\|/ { ## match lines with a zero before the pipe 

    ### surround tailing digits with() 
    ## 
    s/(\w\w) (\w\w) (\w\w) (\w\w) (\w\w)$/(\1) (\2) (\3) (\4) (\5)/; 

    ### replace the zeroes (00) with 00 
    ## 
    s/\(00\)/00/g; 

}' txt 
 
    foobar 42     | ff 00 00 00 00 
    foobaz 00     | (0a) 00 (0b) 00 00 
    foobie 00     | 00 00 00 00 00 
    bar 00     | (ab) (ba) 00 (cd) 00 

OK!

+0

+1:整齊 - 將所有數字對映射到'(xx)',然後用'00'替換所有的'(00)'。太好了! –

3

這可能會爲你工作(GNU SED):

sed -r '/^\s*\S+\s*00/!b;s/\b([^0][^0]|0[^0]|[^0]0)\b/(&)/g' file 

此忽略不與一個字,接着00.然後,它把括號輪2字符串,它們既不是0或含有開始的行0和非0。

+0

致命的甜蜜! – Dru