在(仅)第一次出现的模式(包括模式)之间进行修改并继续处理

在(仅)第一次出现的模式(包括模式)之间进行修改并继续处理

关于仅第一次出现的两个模式之间的匹配存在几个问题(例如,) - 但它们似乎都依赖于exit,这不好,因为我想继续处理该文件。

我想要做的是修改模式之间范围的第一次出现(但不是其他出现)并执行其他 awk foo.具体来说,我正在尝试注释/注释 dovecot conf 文件的某些部分。在一个真正理想的世界中,我想要一行*来确保该userdb { driver = prefetch }块未被注释,并且所有其他userdb { }块都被注释。但由于该文件至少是可以预测的,因此我的目标是取消注释第一个注释块,然后注释所有其他块。

我有

... some stuff that should be echoed as is ...
#userdb {
#  driver = prefetch
#}
... more stuff
userdb {
  driver = sql
  args = /etc/dovecot/dovecot-sql.conf.ext
}
... still more stuff
#userdb {
  #driver = static
  #args = uid=vmail gid=vmail home=/var/vmail/%u
#}

而且我要

... original stuff ...
userdb {
  driver = prefetch
}
... more stuff
#userdb {
#  driver = sql
#  args = /etc/dovecot/dovecot-sql.conf.ext
#}
... still more stuff
#userdb {
  #driver = static
  #args = uid=vmail gid=vmail home=/var/vmail/%u
#}

我以为我会在 awk 中设置一个标志来达到某个目的,因此:

awk '/^#userdb/,/^#}/ && !ididit {
        print substr($0,2);
        next;
        ididit=1;
    }
    /^userdb/,/}/ {
        print "#", $0;
        next
    }
    {print}' auth-sql.conf.ext

这是非常接近的,但是设置我的ididit标志(我添加了一堆打印,只是为了确保它确实被设置)并没有减少它:

... original stuff ...
userdb {
  driver = prefetch
}
... more stuff
# userdb {
#   driver = sql
#   args = /etc/dovecot/dovecot-sql.conf.ext
# }
... more stuff ...
userdb {
 #driver = static
 #args = uid=vmail gid=vmail home=/var/vmail/%u
}

请注意,最后一个userdb {}块设法取消了自身的注释。这非常令人烦恼,直到我发现深入肠子GNU awk 文档这个小宝石:

echo Yes | awk '/1/,/2/ || /Yes/'

这个程序的作者的本意是(/1/,/2/) || /Yes/。然而,awk 将其解释为 /1/, (/2/ || /Yes/).这无法更改或解决;范围模式不与其他模式组合。

所以,我的孩子&& !ididit是一个无用的人。

关于如何让这一切(或者更好的是我上面的“在一个真正理想的世界中”)发生在一行*上,有什么建议吗?perl不是一个选择,但sed甚至bash可以是。

*“单行”可以是相当复杂的行,尽管我仍然希望它易于理解,并抛出合理的退出代码 - 我正在尝试将其放入 AWS CloudFormation 模板中。

FWIW

$awk -V
GNU Awk 4.0.2

谢谢!

答案1

似乎有一种哲学认为,您应该在 awk脚本中的“模式”(即 之前的内容{)中进行尽可能多的测试。早在遇到 之前,我就已经使用 C 和其他语言进行编程,因此在合适的时候awk我并不不愿意使用一个 (或三个)语句。if

所以这就是我想出的:

awk '/userdb/,/}/ {
        if (!comment) {                         # Uncomment the first one
                sub("#", "")
                print
                if ($0 ~ /}/) comment=1         # All others get commented
        } else {
                if ($0 ~ /^[[:blank:]]*#/) {    # Already commented out?
                        print
                } else {
                        print "#" $0
                }
        }
        next
      }
      { print }
    '

这显然是基于你的代码。  comment是一个类似于你的变量ididit;它最初为 0(假),并在处理第一个块后设置为 1(真)。

在一个/userdb/-/}/块内,

  • 如果我们取消评论,
    • 用于sub删除 a (#如果有)。这比无条件撕掉第一个字符更安全——如果代码是已经未注释?
    • 当我们看到}(标记块的结尾)时,设置标志comment,这样所有未来的匹配都将被注释掉。
  • 如果我们评论的话
    • 如果该行已被注释,则按原样打印,
    • 否则,#在前面加一个。

请注意,取消注释的代码假定第一个块没有包含任何意见。例如,

userdb {
  driver = prefetch     # We want to keep this uncommented.
}

将更改为

userdb {
  driver = prefetch      We want to keep this uncommented.
}

看起来这是一个错误。

相关内容