sed 双重条件?匹配行首,包含文本,然后追加

sed double conditional? Match start of line, containing text, then append

我有一个配置文件,我想在脚本中编辑,有几行,但格式基本上是:

...
admin_users = alice, bob
admin_groups = accounting, finance
...

如果 admin_users 行中不存在用户,我想将其添加到该行。

据我了解,我基本上需要结合两个条件...

/^admin_users/s/$/, charlie/ = 添加 , charlie 到以 admin_users 开头的行 /charlie/!s/$/, charlie/ = 将 , charlie 附加到不包含 charlie

的行

我试过谷歌搜索,但找不到任何相关内容。如果可能的话,语法和简短的解释将不胜感激。

为了捕捉更多特殊情况,让我们使用这个测试文件:

$ cat file
...
admin_users = alice, bob
admin_users = alice, charlieston
admin_users = alice, charlie, bob
admin_users = alice, bob, charlie
admin_groups = accounting, finance
...

综上所述,前两个admin_users需要加上charlie。后面两个已经有他了

仅当他不在时才添加charlie

$ sed -r '/^admin_users .*, charlie(,|$)/{p;d}; /^admin_users / s/$/, charlie/' file
...
admin_users = alice, bob, charlie
admin_users = alice, charlieston, charlie
admin_users = alice, charlie, bob
admin_users = alice, bob, charlie
admin_groups = accounting, finance
...

工作原理

  • -r

    使用扩展的而非基本的正则表达式。 (在 OSX/BSD 上,改用 -E。)

  • /^admin_users .*, charlie(,|$)/{p;d}

    如果 admin_users 已经有 charlie 后跟逗号或行尾,则打印,p,按原样删除,d,这样就不会对其应用更多命令。

  • /^admin_users / s/$/, charlie/

    如果我们到达这里,这意味着 charlie 还没有上线。因此,我们在末尾添加 charlie

Mac OSX

以上是在 GNU sed 上测试的。对于 OSX/BSD sed,尝试:

sed -E -e '/^admin_users .*, charlie(,|$)/{p;d;}' -e '/^admin_users / s/$/, charlie/' file

变体

正如 Glenn Jackman 所建议的,这是另一种方法:

sed '/^admin_users\>/ {/\<charlie\>/! s/$/, charlie/}' file

这将选择以 admin_users 开头的行,然后仅当该行不包含 charlie 时才进行替换。 \<\> 是表示单词开头或结尾的正则表达式。它们至少得到 GNU sed 的支持。

有 GNU sed 我会用这个:

sed '/^admin_users/{/\bcharlie\b/!s/$/, charlie/}' input

解释:

/^admin_users/ 搜索以 admin_users 开头的行,并将花括号 {} 之间的以下块应用于该行。 /\bcharlie\b/! 检查该行不包含单词 charlie。请注意单词边界 \b,它确保 charlie2bigcharlie 不匹配。

如果确定该行不包含术语 charlie,则 s/$/, charlie 命令将 , charlie 附加到行 $.

的末尾

在 MacOS 上,单词边界的 \b 将不起作用。您需要对左边界使用 [[:<:]],对右边界使用 [[:>:]]。该命令如下所示:

sed '/^admin_users/{/[[:<:]]charlie[[:>:]]/!s/$/, charlie/}' input