3

我知道默认FS" "(一个空格),这是一种特殊情况,意思是“空格、制表符和换行符”,默认OFS" "(一个空格)。

是否可以知道分隔两个特定字段的确切字符串,或者更一般地说,让输出格式以给定输入的方式反映输入格式

$ 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
4

3 回答 3

3

如果不使用 OFS 的值作为分隔符重新编译记录,则无法将值分配给字段。相反,使用正则表达式来描述整个记录并替换您关心的字段所在的记录部分。例如使用 GNU awk(在其他 awk 中 - 使用 match()/substr() 和 [[:space:]]):

$ 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

更改1in{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() 的函数,它的工作方式与 split() 类似,但它不仅将字段存储在结果字符串中,它还将字段之间的空格存储在第二个数组中,因此您可以在这些数组上使用循环来获取如果更清楚,原始空间:

$ 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:>
于 2013-05-06T14:01:33.253 回答
3

sub,gsub或者gensub可以在这种情况下工作,但只能在 上执行此操作$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
于 2013-05-06T14:07:46.200 回答
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.

于 2013-05-06T14:09:47.220 回答