如何在删除 awk 中的最后一个字段时保留前导空格?



我想创建一个包含动态凭据的配置文件模板。副本应保留结构,但值替换为空字符串(即 "(。我为此目的使用awk

awk -v dq=""" '{ if($NF ~ /^[&|*|n]/ || $1 == $NF) print $0; else {$NF=""; print $0 dq dq;} }' .config.yaml >> .config_temp.yaml

问题出在 else 语句中。当我设置$NF=""前导空白不再打印时。如果我没有将最后一个字段设置为空字符串,我就不会观察到这种行为,但显然不会收到预期的输出(见下文(。

如何在保留前导空格的同时将最后一个字段设置为空字符串?

我不想手动添加许多空格,因为意图各不相同。

config.yaml(Input(


DEVELOPMENT: &development
<<: *common
check_access_token: False
database:
mongodb:
database: test
hostname: localhost
port: 27017
username: ""
password: ""
collection:
col_1: test_1
col_2: test_2
col_3: test_3
col_4: test_4

conf_temp.yaml(实际输出(


DEVELOPMENT: &development
<<: *common
check_access_token: ""
database:
mongodb:
database: ""
hostname: ""
port: ""
username: ""
password: ""
collection:
property: ""
ctrl_voc: ""
form: ""
user: ""

预期产出


DEVELOPMENT: &development
<<: *common
check_access_token: ""
database:
mongodb:
database: ""
hostname: ""
port: ""
username: ""
password: ""
collection:
property: ""
ctrl_voc: ""
form: ""
user: ""

编辑(在Sundeep的重播之后(

谢谢你的回答。它几乎按照我的预期工作。但是,我收到的输出与您不同。如果我打电话

awk -F'[ ]' -v dq=""" 'NF>1 && $NF !~ /^[*&]|:$/{$NF = dq dq} 1' .conf.yaml

我收到以下输出:

DEVELOPMENT: &development
<<: *common
check_access_token: ""
""                             <--
""                           <--
database: ""
hostname: ""
port: ""
username: ""
password: ""
""                         <--
property: ""
ctrl_voc: ""
form: ""
user: ""

缩进符合预期,但上层的键被引号取代(请参阅箭头(。

如果我将您的第二个建议与sed一起使用,我会收到相同的输出.

请参阅 awk 的默认字段分隔符,以了解当FS具有默认值或设置为单个空格字符时会发生什么情况。

您可以通过使用其他方式来传达单个空格来避免它,例如[ ]

$ awk -F'[ ]' -v dq=""" 'NF>1 && $NF !~ /^[*&]|:$/{$NF = dq dq} 1' ip.txt
DEVELOPMENT: &development
<<: *common
check_access_token: ""
database:
mongodb:
database: ""
hostname: ""
port: ""
username: ""
password: ""
collection:
col_1: ""
col_2: ""
col_3: ""
col_4: ""
  • NF>1避免更改空行
  • $NF !~ /^[*&]|:$/检查最后一个字段是否以*&开头,或者是否以:结尾
  • 如果同时满足上述两个条件,请将"最后一个字段"设置为"""
  • 1是打印$0内容的惯用方法


对于给定的示例,您还可以使用:

sed '/:$/! s/ [^*&][^ ]*$/ ""/' ip.txt

问题:如何在不更改原始字段分隔符的情况下更新字段。

根据 awk POSIX 标准,当您使用$i = expr更新字段时,它会导致重新计算 $0 的值,字段由OFS的值分隔。

对于任何不是 ere的字段分隔符,解决方案很简单。更改字段n按如下方式完成:

awk 'BEGIN{FS=OFS="string"}
{$n="new_value"}
{ ... }' file

对于其他字段分隔符,这有点问题:

  • 如果FS=" "(默认值(,则忽略记录前后的任何间距,使用制表符和字段的任意组合
  • 如果FS="ere"扩展正则表达式,您并不真正知道字段分隔符是什么。如果FS="fo*,它可以是从ffooooooo的任何内容。

在POSIX awk中,你需要做一些讨厌的操作:

awk 'BEGIN{FS="ere"}
# split original record
{ split($0,a,FS) }
# update field value
{ a[n]="new_value" }
# rebuild record
{
match($0,$1); rec=substr($0,1,RSTART-1); t=substr($0,RSTART+RLENGTH)
for(i=1;i<NF;i++) {
match(t,$(i+1)); rec = rec a[i] substr(t,1,RSTART-1)
t=substr(t,RSTART+RLENGTH)
}
$0 = rec a[NF] t
}
{ ... }' file

在 GNU awk 中,你可以以更通用的方式使用split,因为它有一个扩展来保存原始分隔符:

awk 'BEGIN{FS="ere"}
# split original record
{ split($0,a,FS,f) }
# update field value
{ a[n]="new_value" }
# rebuild record
{ rec=f[0]; for(i=1;i<=NF;i++) rec=rec a[i] f[i]; $0 = rec }
{ ... }' file

一般评论:GNU awk 确实可以从反转split命令并执行combine的例程中受益

最新更新