【问题标题】:sed command to remove first occurrence of a wordsed 命令删除第一次出现的单词
【发布时间】:2015-06-04 01:27:07
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: macos sed


    【解决方案1】:

    使用 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;}'

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

    • -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 对 {print $0} 的神秘简写。

    【讨论】:

    • 给我这个错误:sed: 2: "s/admin,//1": unexpected EOF (pending }'s)
    • OP 使用的是带有 BSD sed 的 MacOS X。使用 BSD sed,您不能(或者至少我没有找到方法)在单行中使用 b 指令。试试sed -i.bak -n '1h; 1!H; $ { x; s/admin,//; p; }' file(它的工作方式基本相同,但在匹配之前使用不同的方式组装整个文件)。
    • 两者都可以。 BSD sed 不只接受 -i 没有参数,但(但 -i '' 有效)。顺便说一句:b 指令在 BSD sed 的单行中不起作用的原因是,ba; }; stuff 之类的东西将 b 之后的所有内容作为标签名称,并尝试跳转到标签a; }; stuff。这很愚蠢,因为我认为 BSD sed 不可能声明名称中包含分号的标签,但我有错误消息来证明这一点:sed 'ba; stuff' 给出了sed: 1: "ba; stuff": undefined label 'a; stuff'
    • 在 BSD sed 中使用 ; 链接命令很好。与{} 相比,要知道的是,对于BSD sed,您需要; 在GNU sed 不需要的} 之前({p}{p;})。至于代码:1h 如果是第一行,则将当前行复制到保持缓冲区。 1!H 如果不是第一个,则将其附加在那里。如果当前行是最后一行,$ { ... } 会做一些事情(此时整个文件都在保持缓冲区中)。 x 交换保持缓冲区和模式空间,s/admin,// 从 PS 中删除第一个 admin,p 打印 PS。 -n 选项禁用自动打印。
    • @Technic1an 我为 Wintermute 的命令添加了解释,并更新了 awk 命令以就地更改文件。
    【解决方案2】:

    使用 sed,无需读取整个文件:

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

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

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

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

    【讨论】:

      猜你喜欢
      • 2022-01-27
      • 2013-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多