【问题标题】:How can I keep the leading white spaces while removing last field in awk?如何在删除 awk 中的最后一个字段时保留前导空格?
【发布时间】:2020-01-23 15:48:59
【问题描述】:

我想创建一个包含动态凭据的配置文件模板。副本应保留结构,但将值替换为空字符串(即“”)。为此,我使用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(输入)


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 一起使用,我会收到相同的输出。

【问题讨论】:

    标签: bash awk


    【解决方案1】:

    请参阅 Default field separator for 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&gt;1 避免更改空行
    • $NF !~ /^[*&amp;]|:$/ 检查最后一个字段是否不以 *&amp; 开头或不以 : 结尾
    • 如果以上两个条件都满足,设置最后一个字段为""
    • 1 是打印$0 内容的惯用方式


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

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

    【讨论】:

    • 我没有收到与您相同的输出。请参阅我的问题的扩展。
    • 也许你有dos风格的行尾? file .config.yaml 的输出是什么?如果它是 dos 结尾,那么使用 :\r$ 而不是 :$ 会有所帮助.. 另请参阅:stackoverflow.com/questions/45772525/…
    【解决方案2】:

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

    根据 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 的例程

    【讨论】:

      猜你喜欢
      • 2012-09-19
      • 2010-09-29
      • 2015-07-04
      • 2014-08-22
      • 2013-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多