2013-05-06 39 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