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
,它确保 charlie2
或 bigcharlie
不匹配。
如果确定该行不包含术语 charlie,则 s/$/, charlie
命令将 , charlie
附加到行 $
.
的末尾
在 MacOS 上,单词边界的 \b
将不起作用。您需要对左边界使用 [[:<:]]
,对右边界使用 [[:>:]]
。该命令如下所示:
sed '/^admin_users/{/[[:<:]]charlie[[:>:]]/!s/$/, charlie/}' input
我有一个配置文件,我想在脚本中编辑,有几行,但格式基本上是:
...
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
,它确保 charlie2
或 bigcharlie
不匹配。
如果确定该行不包含术语 charlie,则 s/$/, charlie
命令将 , charlie
附加到行 $
.
在 MacOS 上,单词边界的 \b
将不起作用。您需要对左边界使用 [[:<:]]
,对右边界使用 [[:>:]]
。该命令如下所示:
sed '/^admin_users/{/[[:<:]]charlie[[:>:]]/!s/$/, charlie/}' input