sed命令删除第一次出现的单词

sed command to remove first occurrence of a word

我想知道如何删除文件中第一次出现的单词。

示例:我想删除第一次出现的管理员。

account    required       pam_opendirectory.so
account    sufficient     pam_self.so
account    required       pam_group.so no_warn group=admin,wheel fail_safe
account    required       pam_group.so no_warn deny group=admin,wheel ruser fail_safe

我试过:

sudo sed -i.bak "s/admin,//1" /etc/pam.d/screensaver

但这两种情况都删除了,有什么想法吗?

使用 sed

要删除文件中的第一个匹配项:

$ sed -e ':a' -e '$!{N;ba;}' -e 's/admin,//1' file
account    required       pam_opendirectory.so
account    sufficient     pam_self.so
account    required       pam_group.so no_warn group=wheel fail_safe
account    required       pam_group.so no_warn deny group=admin,wheel ruser fail_safe

(以上是用 GNU sed 测试的。)

工作原理

  • -e ':a' -e '$!{N;ba;}'

    这会立即将整个文件读入模式 space。 (如果您的文件对内存来说太大,这不是一个好方法。)

  • -e 's/admin,//1'

    这会在 admin 第一次出现且仅在第一次出现时执行替换。

使用 BSD (OSX) sed

Wintermute 报告 BSD sed 不能在单行中执行分支指令,并建议使用此替代方法来更改文件:

sed -i.bak -n '1h; 1!H; $ { x; s/admin,//; p; }' file

这会立即读取整个文件,然后在读取最后一行后进行替换。

更详细:

  • -n

    默认情况下,sed 会打印每一行。这将关闭它。

  • 1h

    这会将第一行放入保留缓冲区。

  • 1!H

    对于所有后续行,这将它们追加到保留缓冲区。

  • $ { x; s/admin,//; p; }

    对于最后一行 ($),这将交换保持和模式缓冲区,以便模式缓冲区现在具有完整的文件。 s/admin,// 进行替换,p 打印结果。

使用 awk

$ awk '/admin/ && !f{sub(/admin,/, ""); f=1} 1' file >file.tmp && mv file.tmp file

这导致:

account    required       pam_opendirectory.so
account    sufficient     pam_self.so
account    required       pam_group.so no_warn group=wheel fail_safe
account    required       pam_group.so no_warn deny group=admin,wheel ruser fail_safe

工作原理

  • /admin,/ && !f{sub(/admin,/, ""); f=1}

    对于每一行,检查它是否包含单词 admin, 以及标志 f 是否仍然具有默认值零。如果是这样,请删除第一次出现的 admin, 并将标志设置为一个。

  • 1

    打印每一行。 1 是 awk 的神秘 shorthand for {print [=29=]}.

使用 sed,不读取整个文件:

sed 's/admin,//;tl;b;:l;n;bl' file

这会等待成功替换,然后进入打印文件其余部分的循环。

  • s/admin,//;tl;b;:用空字符串替换admin,如果替换发生则跳转到标签l否则打印该行并退出(下一行将以同样的方式处理)。

  • :l;n;bl:定义标签l,读取行,跳转到l。