【问题标题】:How to use sed to edit after the first occurrence of a certain character?在第一次出现某个字符后如何使用 sed 进行编辑?
【发布时间】:2020-07-31 18:10:04
【问题描述】:

我创建了一个非常复杂的 sed 命令来删除匹配某些模式的部分:

sed 's/...//Ig; s/...//Ig; s/...//Ig'

但我发现我犯了一个错误,我应该只编辑第一次出现:之后的部分。如何修改这个 sed 命令和/或使用其他命令来实现它?

要编辑的行实际上是 grep 的输出,例如:

/foo/bar:foobar
/foobar/foo/bar:foo_bar

【问题讨论】:

    标签: regex bash awk sed


    【解决方案1】:

    awk 可以在这里作为更好的选择:

    awk 'BEGIN{FS=OFS=":"} {s=$1; $1=""; gsub(/a/, "@"); gsub(/o/, "0"); print s $0}' file
    

    /foo/bar:f00b@r
    /foobar/foo/bar:f00_b@r
    

    在这里,我们使用: 拆分输入,并将: 之前的第一个字段保存在变量s 中。然后我们运行几个gsub 函数来进行所有替换,最后我们打印保存的变量和行的其余部分。

    【讨论】:

    • 嗨 anubhava,sed 命令相当复杂: sed 's/((at[[:blank:]]\+)\|(@[[:blank:]]*))foobar [[:blank:]]*(\.\|dot\|\.dot\.)[[:blank:]]*com//Ig; s/copyright[[:blank:]]*((c)\|©)*[-–[:blank:][:digit:]~,by]*foo[[:blank:]]*bar//免疫球蛋白; s/com\.foobar\.//g',我不确定如何使用 gsub 进行相同的模式匹配
    • 相同的sed模式和替换可以直接在gsub函数中使用。
    【解决方案2】:

    为了简单起见,我假设您想用FOO 替换第一个: 之后发生的每个foo

    sed 'h           # save the current line in the hold space
         s/[^:]*://  # delete everything up to the marker
         s/foo/FOO/g # YOUR COMPLICATED COMMAND GOES HERE
         x           # swap pattern and hold space
         s/:.*/:/    # delete from the first : to the end in the original line
         G           # append hold space (: with whatever follows it)
         s/\n//' yourfile # remove the newline that comes with G
    

    以上代码是根据评论中收到的建议更新的。

    原始答案如下。即使在这种情况下有点矫枉过正,但它表明您可以使用sed\x0 中的空字符作为您通常可以假设不在文本文件中的“标记”(与使用例如 _xxx_ 可能已经在文件中)。 (第二个版本替换了foo 之前的出现 :,与我误读问题时一致。

    sed 'h           # save the current line in the hold space
         s/:/\x0:/   # mark the first : by prepending a null character
         s/.*\x0//   # delete everything up to the marker
         x           # swap pattern and hold space
         s/:.*//     # delete from the first : to the end in the original line
         s/foo/FOO/g # YOUR COMPLICATED COMMAND GOES HERE
         G           # append hold space (: with whatever follows it)
         s/\n//' yourfile # remove the newline that comes with G
    

    【讨论】:

    • 要删除第一个:之前的所有内容,你可以这样做s/[^:]*:/:/
    • @Philippe,完全正确!我现在感觉很愚蠢,啊哈。
    • \x0 在很多场合都能派上用场。
    • 感谢您的评论;我真的应该尝试从我的肌肉记忆中删除.*,因为它让我认为我需要模拟一个非贪婪的*,而不是将正常/贪婪的应用到适当的令牌。
    • @WalterA,谢谢。我还应该花更多时间更仔细地阅读问题。
    【解决方案3】:

    首先将grep 输出的每一行分成两行,然后在偶数行上执行sed 命令。
    它看起来像

    grep "something" list_of_file |
      sed 's/:/\n/' |
      sed '0~2s/...//Ig; 0~2s/...//Ig; 0~2s/...//Ig' |
      paste -d":" - -
    

    使用0~2,您是在告诉sed 仅对偶数行进行操作。
    示例:

    grep -E "root|127" /etc/{passwd,hosts} |
       sed 's/:/\n/' |
       sed -r '0~2s/([0,o])/==\1==/g' | 
       paste -d":" - -
    

    输出:

    /etc/passwd:r==o====o==t:x:==0==:==0==:r==o====o==t:/r==o====o==t:/bin/bash
    /etc/hosts:127.==0==.==0==.1    l==o==calh==o==st
    

    【讨论】:

      【解决方案4】:

      这可能对你有用(GNU sed):

      sed 's/:/\n&/;h;s/foo/FOO/g;s/bar/BAR/g;y/-/_/;H;g;s/\n.*\n//' file
      

      在第一个 : 之前引入一个换行符。

      将结果复制到保持空间 (HS)。

      全局替换/翻译一次或多次。

      将模式空间 (PS) 附加到 HS。

      用 HS 代替 PS。

      删除换行符和它们之间的所有内容。

      【讨论】:

        【解决方案5】:

        您可以尝试 Perl 替代方案。这是一种使用正面向后看的解决方案

        $ cat grep_out.dat
        /foo/bar:foobar
        /foobar/foo/bar:foo_bar
        $ perl -pe ' s/(?<=:)(foo)/\U\1/g ' grep_out.dat
        /foo/bar:FOObar
        /foobar/foo/bar:FOO_bar
        $
        

        【讨论】:

          猜你喜欢
          • 2021-12-05
          • 2019-01-31
          • 2020-04-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-06-08
          • 2022-08-24
          • 1970-01-01
          相关资源
          最近更新 更多