在awk中,字段(或记录)分隔符FS
(或RS
)可以设置为正则表达式。
它非常适合获取任何单个字段,但是一旦设置了这些字段,字段分隔符就会“消失”。
echo "a|b-c|d" | awk 'BEGIN{FS="[|-]"} {$3="z"}1'
a b z d
在这种情况下,输出字段分隔符OFS
默认设置为空格。
不幸的是,这种语句OFS=FS="[|-]"
无效,因为它将OFS
设置为一个字符串。
据我所知,如果有多个选择,awk选择输出字段分隔符可能会变得棘手,但是如果没有新字段,则可以保留当前的字段。
那么,是否有一种简单的方法可以将OFS
设置为与FS
完全相同的正则表达式,这样我就可以了?
echo "a|b-c|d" | awk '... {$3="z"}1'
a|b-z|d
或者,有没有办法捕获数组中的所有分隔符?例如?
同样的问题也适用于记录分隔符RS
(及其关联的ORS
)
答案 0 :(得分:5)
正如您已经提到的,无法根据每个案例中使用的OFS
动态设置FS
。如果正则表达式位于RS
而不是FS
,则可以使用RT
(事实上,我只看到anubhava的答案就是这样,很好!)。
但是,如果你有GNU awk还有另外一种方法:如column replacement with awk, with retaining the format (Ed Morton's answer)所示,你可以使用split()
,特别是第四个参数。为什么?因为它在每个切片之间存储分隔符:
gawk 'BEGIN{FS="[|-]"} # set FS
{split($0, a, FS, seps) # split based on FS and ...
# ... store pieces in the array seps()
a[3]="z" # change the 3rd field
for (i=1;i<=NF;i++) # print the data back
printf "%s%s", a[i], seps[i] # keeping the separators
print "" # print a new line
}'
作为单行:
$ gawk 'BEGIN{FS="[|-]"} {split($0, a, FS, seps); a[3]="z"; for (i=1;i<=NF;i++) printf "%s%s", a[i], seps[i]; print ""}' <<< "a|b-c|d"
a|b-z|d
split(string,array [,fieldsep [,seps]])
将字符串分成由fieldsep分隔的片段,并将片段存储在数组中,将分隔符字符串存储在seps数组中。第一部分存储在数组1中,第二部分存储在数组2中,依此类推。第三个参数fieldsep的字符串值是描述拆分字符串的位置的正则表达式(就像FS可以是描述拆分输入记录的位置的正则表达式一样)。如果省略fieldsep,则使用FS的值。 split()返回创建的元素数。 seps是一个gawk扩展,seps [i]是array [i]和array [i + 1] 之间的分隔符字符串。如果fieldsep是单个空格,则任何前导空格都进入seps [0],任何尾随空格进入seps [n],其中n是split()的返回值(即数组中元素的数量)。 / p>
答案 1 :(得分:3)
awk
(其中N是字段编号)更改任何字段值,则 OFS
会使用$N=<whatever>
重写每条记录。
由于您在FS
中使用了多个分隔符,因此无法使用OFS=FS
。
如果您有gnu awk
,那么您可以使用基于RS
和RT
的解决方案:
s="a|b-c|d"
awk -v RS='[-|]' 'NR==3{$0="z"} {printf "%s%s", $0, RT}' <<< "$s"
a|b-z|d
或者,您可以使用sed
:
s="a|b-c|d"
sed -E 's/^(([^|-]+[|-]){2})[^|-]+/\1z/' <<< "$s"
a|b-z|d
答案 2 :(得分:0)
由于您显然不需要处理字段,因此只需处理$ 0其他方式,例如下面的sub
:
$ echo "a|b-c|d" | awk 'BEGIN{FS="[|-]"} {sub(/c/,"z")}1'
a|b-z|d