【问题标题】:Remove characters between two patterns using sed使用 sed 删除两个模式之间的字符
【发布时间】:2020-01-19 13:26:42
【问题描述】:

我有以下用例,我需要使用 sed 删除一些特殊字符 (",/,\)。

sample.txt

srcs : [a.c] cflags : [abcd@ef]
srcs : ["b.c"] cflags : [ab\cd"ef]
srcs : [r/.c] cflags : [a""bcd*ef""]
srcs : [g.c] cflags : [ab/cd*ef]

需要从每行的 cflags 条目中删除 (\,",/)。预期输出:

srcs : [a.c] cflags : [abcd@ef]
srcs : ["b.c"] cflags : [abcdef]
srcs : [r/.c] cflags : [abcd*ef]
srcs : [g.c] cflags : [abcd*ef]

尝试使用正则表达式的 sed 替换机制,它在 "cflags" 和 "]" 之间搜索字符串,返回 cflags 和 ] 之间的字符串:

cat sample.txt | sed 's/cflags : \(.*\)]/\1/'

srcs : [a.c] [abcd@ef
srcs : ["b.c"] [ab\cd"ef
srcs : [r/.c] [a""bcd*ef""
srcs : [g.c] [ab/cd*ef

替换为 '' 但它会删除整个 cflags 条目:

cat sample | sed 's/cflags : \(.*\)]/''/'

srcs : [a.c]
srcs : ["b.c"]
srcs : [r/.c]
srcs : [g.c]

寻找一个可以在每一行的 cflags 和 ']' 之间找到 (\,",/) 的正则表达式,然后可以使用 sed 将其删除。

【问题讨论】:

    标签: shell awk sed


    【解决方案1】:

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

    sed -E ':a;s/(.*cflags.*)["\\/]/\1/;ta' file
    

    将包括cflags 在内的所有内容保留在一行中,然后删除任何\/" 字符。

    如果必须只修改第二对方括号之间的行,请使用:

    sed -E ':a;s/(.*cflags[^[]*\[[^]]*)[\\/"]/\1/;ta' file
    

    【讨论】:

      【解决方案2】:

      在 sed 中不容易,但在 Perl 中可能:

      perl -pe '
          s{cflags : \[\K([^]]*\])}{
              $1 =~ s,[\\/"],,gr
          }e' -- sample.txt
      
      • -p逐行读取输入并打印结果
      • s{pattern}{replacement} 类似于 sed 的 s///,但更强大
      • e 修饰符将替换解释为代码
      • \K 忘记了到目前为止匹配的任何内容,因此 cflags 部分匹配,但没有被替换
      • r 修饰符返回替换的结果,而不是原地更改变量

      【讨论】:

        【解决方案3】:

        如果您对awk 没问题,请尝试关注:

        awk '
        match($0,/cflags[^]]*\]/){
          val=substr($0,RSTART,RLENGTH)
          gsub(/\\|,|"|\//,"",val)
          print substr($0,1,RSTART-1) val substr($0,RSTART+RLENGTH)
          val=""
        }
        '  Input_file
        

        以上代码说明:

        awk '                                                          ##Starting awk program from here.
        match($0,/cflags[^]]*\]/){                                     ##Using match to match regex from cflags till ] here.
          val=substr($0,RSTART,RLENGTH)                                ##Creating variable val which is sub-string of RSTART and RLENGTH values.
          gsub(/\\|,|"|\//,"",val)                                     ##Globally substituting \,"/ with NULL in variable val here.
          print substr($0,1,RSTART-1) val substr($0,RSTART+RLENGTH)    ##Printing before part, actual part and last part of lines here,in this program.
          val=""                                                       ##Nullifying variable val here.
        }
        '  Input_file                                                  ##Mentioning Input_file name here.
        

        输出如下。

        srcs : [a.c] cflags : [abcd@ef]
        srcs : ["b.c"] cflags : [abcdef]
        srcs : [r/.c] cflags : [abcd*ef]
        srcs : [g.c] cflags : [abcd*ef]
        

        【讨论】:

          【解决方案4】:

          您不能将正则表达式仅应用于 sed 中字符串的一部分。所以做到这一点的方法是将线保持在保持空间中。然后提取您需要应用正则表达式的字符串部分。然后应用正则表达式 - 即。删除 " / \ 字符。然后从保持空间中取出行并重新洗牌,以便用替换的部分替换您想要替换的字符串部分(哦,我希望这是有道理的)。

          以下脚本:

          cat <<'EOF' |
          srcs : [a.c] cflags : [abcd@ef]
          srcs : ["b.c"] cflags : [ab\cd"ef]
          srcs : [r/.c] cflags : [a""bcd*ef""]
          srcs : [g.c] cflags : [ab/cd*ef]
          EOF
          sed '
              # hold the line
              h
              # remove everything before clags
              s/.*cflags : //
              # replace the " \ / for nothing, ie. remove them
              # alternatively s/\("\|\\\|\/\)//g or s@\("\|\\\|/\)@@g 
              # but I think the following is more readable
              s/"//g
              s/\\//g
              s/\///g
              # append the holded line
              G
              # shuffle the pattern space for the output
              s/\(.*\)\n\(.*\)cflags : .*/\2cflags : \1/
          '
          

          outputs on repl:

          srcs : [a.c] cflags : [abcd@ef]
          srcs : ["b.c"] cflags : [abcdef]
          srcs : [r/.c] cflags : [abcd*ef]
          srcs : [g.c] cflags : [abcd*ef]
          

          还有一个单线:

          sed 'h;s/.*cflags : //;s/"//g;s/\\//g;s/\///g;G;s/\(.*\)\n\(.*\)cflags : .*/\2cflags : \1/'
          

          【讨论】:

            最近更新 更多