2013-05-06 43 views
3

我知道默認的FS" "(單個空格),這是一個特殊情況,意思是「空格,製表符和換行符」,並且默認爲OFS" "(單個空間)。awk的輸出反映字段分隔輸入的可能性

是否有可能知道,通過該兩個特定字段已被分離,或更一般地,完全匹配的字符串,都輸出格式反映在給定了輸入

$ cat foo 
foo bar  quux # single space, single tab 
foo bar quux  # single space, double space, triple space 

的命令的方式輸入格式:

$ awk '{ $2 = "blah" }1' foo 

會產生:

foo blah  quux # single space, single tab 
foo blah quux  # single space, double space, triple space 

代替:

foo blah quux   # single space, single space according to default OFS 
foo blah quux   # single space, single space according to default OFS 
+0

我用你的輸入執行了你的awk行。我的awk(gawk)按照您的預期輸出了輸出。 (單個空格分隔) – Kent 2013-05-06 13:40:30

+0

@Kent您誤解了我的問題,我問是否有辦法獲取第一個輸出(單個輸出字段分隔符=單個輸入字段分隔符)。 – 2013-05-06 13:41:30

+0

哦,對不起... .. – Kent 2013-05-06 13:45:45

回答

3

subgsubgensub可能在這種情況下工作,但只做到這一點上$0,不這樣做就$1-n,因爲它不會觸發重新計算OFS。

但您必須計算空格/製表符以編寫正則表達式模式,以確保替換替換行中正確的文本部分(字段)。 (如@Ed所示)

如果你有GAWK,你可以使用FPAT,它可以通過節省一些努力:

awk 'BEGIN{FPAT="\\s*\\S*\\s*";OFS=""} {sub("\\S*","bar",$2)}1' file 

這會產生你想要的結果。

例如(該<tab>不能被看到,但它的存在)

kent$ cat file 
foo bar qq 
foo bar qqq 
kent$ awk 'BEGIN{FPAT="\\s*\\S*\\s*";OFS=""} {sub("\\S*","xxx",$2)}1' file 
foo xxx qq 
foo xxx qqq 
+0

解決方案如果你改變sub()使用RE分隔符而不是字符串del imiters,那麼你不需要雙重轉義「\ S」:'sub(/ \ S * /,..)'。我不認爲你需要在FPAT上的尾部\\ s *,但請參閱@ sudo_O的解決方案。 – 2013-05-06 14:28:47

+0

@EdMorton是,尾隨'\ s *'可以被保存。 – Kent 2013-05-06 14:50:40

3

如果沒有awk重新編譯使用OFS值作爲分隔符的記錄,您不能將值分配給字段。相反,使用正則表達式來描述整個記錄,並替換存在於您關心的字段所在的記錄部分。例如用GNU AWK(其它awks - 用火柴()/ SUBSTR()和[[:空間:]]):

$ cat foo 
foo bar quux   # single space, single tab 
foo bar quux  # single space, double space, triple space 

$ awk '{ print gensub(/^(\s*(\S+\s+){1})\S+(.*)/,"\\1blah\\3","") }' foo 
foo blah quux   # single space, single tab 
foo blah quux  # single space, double space, triple space 

變化{1}1適合不過許多領域之前要替換領域:

$ awk '{ print gensub(/^(\s*(\S+\s+){2})\S+(.*)/,"\\1blah\\3","") }' foo 
foo bar blah   # single space, single tab 
foo bar blah  # single space, double space, triple space 

$ awk '{ print gensub(/^(\s*(\S+\s+){3})\S+(.*)/,"\\1blah\\3","") }' foo 
foo bar quux   blah single space, single tab 
foo bar quux  blah single space, double space, triple space 

GAWK還包含一個名爲patsplit函數(),其工作方式類似於分裂(),但代替僅存儲所得到的字符串中的字段,它也存儲的字段之間的空間中的第二陣列,從而可以在這些陣列上使用循環以獲得原始空間(如果更清晰):

$ awk '{ nf = patsplit($0,fld,/\S+/,sep); fld[2]="blah"; for (i=1;i<=nf;i++) printf "%s%s", sep[i-1], fld[i]; print "" }' foo 
foo blah quux   # single space, single tab 
foo blah quux  # single space, double space, triple space 

$ awk '{ nf = patsplit($0,fld,/\S+/,sep); fld[3]="blah"; for (i=1;i<=nf;i++) printf "%s%s", sep[i-1], fld[i]; print "" }' foo 
foo bar blah   # single space, single tab 
foo bar blah  # single space, double space, triple space 

以下是如何patsplit()被打破每一條記錄:

$ awk '{ nf = patsplit($0,fld,/\S+/,sep); print "\n" $0; for (i=0;i<=nf;i++) print "<" i ":" fld[i] 
":" sep[i] ">" }' foo 

foo bar quux   # single space, single tab 
<0::> 
<1:foo: > 
<2:bar: > 
<3:quux:   > 
<4:#: > 
<5:single: > 
<6:space,: > 
<7:single: > 
<8:tab:> 

foo bar quux  # single space, double space, triple space 
<0:: > 
<1:foo: > 
<2:bar: > 
<3:quux:  > 
<4:#: > 
<5:single: > 
<6:space,: > 
<7:double: > 
<8:space,: > 
<9:triple: > 
<10:space:> 
+0

你的意思是,「沒有awk使用* O * FS的值重新編譯記錄」?我害怕答案是「否」。我很清楚,我可以使用正則表達式來描述整個記錄,但是重點沒有必要那樣做,這對於複雜的'FS's來說真的很尷尬,因爲它本身可以是一個正則表達式,並且它能夠完成上面的操作是非常簡單的:( – 2013-05-06 14:06:57

+0

感謝您的精心輸入,我沒有這麼做, t知道'patsplit' - 非常豐富! – 2013-05-06 14:59:13

2

還有就是這個問題沒有通用的解決方案,但如果你有GNU awk你可通過巧妙的搭配FPAT由包括前導空格作爲一部分做現場:

$ awk '{sub(/\S+/,"blah",$2)}1' OFS= FPAT='\\s*\\S+' file 
foo blah quux   # single space single tab 
foo blah quux  # single space double space triple space 

該方法的具體問題,爲替代和正則表達式FPAT將需要改變每一個問題,但你不能用awk做得更好。

+0

+1爲解決方案,但爲什麼不使用'sub(/ \ S +/...)'和'FPAT ='\\ s * \\ S +''所以它適用於所有空格分隔的字段? – 2013-05-06 14:25:05

+0

@EdMorton你已經爲我的'patsplit'獲得了+1我最初考慮過使用'split()'兩次來做到這一點,但可選的前導空白混亂了。 – 2013-05-06 14:43:36

+0

很高興你解決了正則表達式,所以它實際上與我提供的輸入,現在我可以upvote沒有感覺不好:-) – 2013-05-06 14:47:28