awk&x27;s使输出反映与字段分离有关的输入的可能性

awk&x27;s使输出反映与字段分离有关的输入的可能性,awk,Awk,我知道默认的FS是”(一个空格),这是一个特例,表示“空格、制表符和换行符”,而默认的OFS是”(一个空格) 是否有可能知道两个特定字段的确切分隔字符串,或者更一般地说,输出格式是否以给定输入的方式反映输入格式 $ cat foo foo bar quux # single space, single tab foo bar quux # single space, double space, triple space 命令: $ awk '{ $2 = "bl

我知道默认的
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

如果awk不使用OFS的值作为分隔符重新编译记录,则无法将值分配给字段。相反,使用regexp来描述整个记录,并替换存在您关心的字段的部分记录。e、 g.使用GNU awk(在其他awk中-使用match()/substr()和[[: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()的函数,该函数的工作方式与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

$awk'{nf=patsplit($0,fld,/\S+/,sep);fld[2]=“blah”;对于(i=1;i
sub
gsub
gensub
可以在这种情况下工作,但只能在
$0
上执行此操作,不要在
$1-n
上执行此操作,因为它不会触发OFS的重新计算

但是为了编写正则表达式模式,您必须计算空格/制表符,以确保替换替换了行中正确的文本部分(字段)

如果您有gawk,您可以使用
FPAT
,它可以通过以下方式节省一些工作量:

 awk  'BEGIN{FPAT="\\s*\\S*\\s*";OFS=""} {sub("\\S*","bar",$2)}1' file
这将生成您想要的结果

e、 g.:(无法看到
,但它在那里)


这个问题没有通用的解决方案,但是如果您有
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

该方法是针对具体问题的,替换的regexp和
FPAT
需要针对每个问题进行更改,但是使用
awk

我用您的输入执行了您的awk行。我的awk(gawk)给出了您期望的输出。(单空格分隔)@肯特:你误解了我的问题,我在问是否有办法获得第一个输出(单个输出字段分隔符=单个输入字段分隔符)。哦,对此感到抱歉……我想你可以使用
awk'BEGIN{FS=“[]””}{$2=“blah”}1'foo
正如在@fedorqui上看到的那样,它只是将
FS
设置为一个实际的单个空格(而不是任何空格),我正在寻找的是:(你的意思是,“没有awk使用OFS值重新编译记录”?我担心答案是“否”。我很清楚,我可以使用正则表达式来描述整个记录,但重点是不必这样做,这对于复杂的
FS
es来说真的很尴尬,因为它本身可以是正则表达式,并且能够做到以上几点是很好的:(感谢您的精心输入,我不知道
patsplit
-非常有用!+1作为解决方案,但是为什么不使用
sub(/\S+/…)
FPAT='\\S*\\S+'
呢?所以它适用于所有空格分隔的字段?@EdMorton您已经从我那里得到了
patsplit
的+1,我最初考虑使用
split()
两次执行此操作,但可选的前导空格会弄乱这一点。很高兴您修复了正则表达式,使其能够与我提供的输入一起实际工作,现在我可以对解决方案进行upvote而不感到不适:-)+1。如果将sub()更改为使用重新分隔符而不是字符串分隔符,则不需要对“\S”进行双重转义:
sub(/\S*/,…)
。不过我认为您不需要FPAT上的尾部\\s*,请参阅@sudo\u O的解决方案。@EdMorton是的,尾部的
\s*
可以保存。
 awk  'BEGIN{FPAT="\\s*\\S*\\s*";OFS=""} {sub("\\S*","bar",$2)}1' file
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
$ 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