AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 686124
Accepted
FelixJN
FelixJN
Asked: 2022-01-13 14:04:36 +0800 CST2022-01-13 14:04:36 +0800 CST 2022-01-13 14:04:36 +0800 CST

仅对子字符串进行更改操作

  • 772

START在一个文件中,在一个由模式和标记的部分之前和之后有任何乱码文本END(特定字符串仅出现一次,并且以正确的顺序出现在同一行上)。我想只对和之间的部分进行一些字符串START操作END

示例输入:

aomodi3hriq32| ¶³r 0q93aoiSTART_this_is_to_be_modified_ENDaqsdofuha23uru| ²23i ii3uhfia
oawpo3<9"§ A hSTART_this_also_needs_modification_ENDqa 032/a237(°1Q"§ >A_this_
START changeme ENDnot_this_modias

在 - 操作方面,应该修改和sed之间的子字符串(和仅子字符串),就像我使用.STARTENDsed 's/_this_// ; s/modi/MODI/ ; y/as/45/'

示例输出:

aomodi3hriq32| ¶³r 0q93aoiSTARTi5_to_be_MODIfied_ENDaqsdofuha23uru| ²23i ii3uhfia
oawpo3<9"§ A hSTART4l5o_need5_MODIfic4tion_ENDqa 032/a237(°1Q"§ >A_this_
START ch4ngeme ENDnot_this_modias

awkwithFS="START|END"失败,因为OFS不能在不同位置设置多个值。

我尝试使用sed嵌套命令替换和不同的分隔符 ( ~) 但失败了,并且还担心之前START/之后可能有字符END会与命令混淆(例如 a /)。这个想法是只选择“内部”子字符串并执行操作,然后将其用作替换的一部分:

sed "s/^\(.*\)START.*END\(.*\)$/\1$(sed 's~^.*START~~
                                         s~END.*~~
                                         s~_this_~~
                                         s~modi~MODI~
                                         y~as~45~' infile)\2/" infile

我不熟悉例如perl....但无论如何。

有没有办法让一组 -sed操作仅适用于一行的 REGEX 匹配的子字符串?

awk text-processing
  • 8 8 个回答
  • 528 Views

8 个回答

  • Voted
  1. Best Answer
    choroba
    2022-01-13T14:39:48+08:002022-01-13T14:39:48+08:00
    perl -CSD -ne '
        if (my ($before, $between, $after) = /^(.*START)(.*)(END.*)/) {
            s/_this_//, s/modi/MODI/, tr/as/45/ for $between;
            print "$before$between$after\n";
        } else { print; }' -- file
    
    • -CSD解码来自 UTF-8 的输入并将输出编码为 UTF-8
    • 除了填充三个变量$before,$between和$after,我们可以使用/pand ${^PREMATCH},${^POSTMATCH}但我没有找到更好的解决方案:
      if (my ($s) = /START(.*)END/p) {
          s/_this_//, s/modi/MODI/, tr/as/45/ for $s;
          print "${^PREMATCH}START${s}END${^POSTMATCH}";
      } else { print; }
      

    如果 START...END 部分可以在一行上重复,则需要遍历每一行。

    for my $part (split /(START.*?END)/) {
        if ($part =~ /^START.*END$/) {
            s/_this_//, s/modi/MODI/, tr/as/45/ for $part;
        }
        print "$part";
    }
    
    • 5
  2. they
    2022-01-14T00:39:07+08:002022-01-14T00:39:07+08:00

    使用标准sed并假设每一行都包含一个START和一个END子字符串(按此顺序):

    # Skip (pass through) lines that does not have START followed by END.
    /.*START\(.*\)END.*/ !b
    
    # Save the original line in the hold space.
    h
    
    # Remove the start and the end from the line.
    # This leaves the bit of the line that we want to modify.
    # (This reuses the previous regular expression.)
    s//\1/
    
    # Modify what's left.
    s/_this_//
    s/modi/MODI/
    y/as/45/
    
    # Append the original line from the hold space,
    # with a newline as delimiter.
    G
    
    # Move the modified bit into the correct spot with a substitution,
    # while deleting the old substring between START and END.
    s/\(.*\)\n\(.*START\).*\(END.*\)/\2\1\3/
    

    测试:

    $ cat file
    aomodi3hriq32| ¶³r 0q93aoiSTART_this_is_to_be_modified_ENDaqsdofuha23uru| ²23i ii3uhfia
    oawpo3<9"§ A hSTART_this_also_needs_modification_ENDqa 032/a237(°1Q"§ >A_this_
    START changeme ENDnot_this_modias
    
    $ sed -f script file
    aomodi3hriq32| ¶³r 0q93aoiSTARTi5_to_be_MODIfied_ENDaqsdofuha23uru| ²23i ii3uhfia
    oawpo3<9"§ A hSTART4l5o_need5_MODIfic4tion_ENDqa 032/a237(°1Q"§ >A_this_
    START ch4ngeme ENDnot_this_modias
    

    内联,在命令行上:

    sed -e '/.*START\(.*\)END.*/!b' -e h -e 's//\1/' \
        -e 's/_this_//' -e 's/modi/MODI/' -e 'y/as/45/' \
        -e G -e 's/\(.*\)\n\(.*START\).*\(END.*\)/\2\1\3/' file
    
    • 4
  3. FelixJN
    2022-01-13T14:17:05+08:002022-01-13T14:17:05+08:00

    也许带有awk和字符串函数:

    awk 'BEGIN{FS="START|END"}
         /START.+END/ {gsub(/_this_/,"",$2)
         gsub(/modi/,"MODI",$2)
         gsub(/a/,"4",$2)
         gsub(/s/,"5",$2)
         print $1"START"$2"END"$3 ; next}
         1' infile
    
    • 3
  4. αғsнιη
    2022-01-13T16:54:32+08:002022-01-13T16:54:32+08:00

    您始终可以构建自己的多个 OFS:

    awk -v FS='START|END' -v OFS= -v map='_this_\r\rmodi\rMODI\ra\r4\rs\r5' '
      BEGIN{ split(FS, mOFS, "|") }
      { n=split(map, tr, "\r"); for(i=1; i<n; i+=2) gsub(tr[i], tr[i+1], $2);
      print $1, mOFS[1], $2, mOFS[2], $3
    }' infile
    

    请注意,gsub() 的第一个参数是正则表达式,因此在定义map=....;时要小心。他们的右手映射也不应该有一些特殊字符,例如&، back-references\1等;但是,当您手动编写映射时,您可以转义任何特殊字符以避免它们被 gsub() 专门解释。

    正如您提到的,我使用 CR\r来分隔映射,这是您输入文件中唯一不存在的东西,除此之外\0,它不能在 split() 和 awk 中的其他函数(或者也可能在其他编程语言中)中使用,因为 awk 会只考虑\0一个字符串中最多可以存在一个。因此,每个左侧的正则表达式(此处为字符串)都将被数组中的tr[i]下一个右侧正则表达式替换。tr[i+1]tr

    使用这种方式将使您免于为每一对编写多个 gsub() 。

    • 3
  5. schrodingerscatcuriosity
    2022-01-13T15:25:08+08:002022-01-13T15:25:08+08:00

    这个 GNUsed给出了想要的结果

    $ sed 's/\(.\)\(START\|END\)/\1\n\2\n/g' file | \
      sed -ne '/START/,/END/s/_this_//' \
      -ne '/START/,/END/y/as/45/' \
      -ne '/START/,/END/s/modi/\U&/g;p' | \
      sed -z 's/\n\(START\|END\)\n/\1/g'
    aomodi3hriq32| ¶³r 0q93aoiSTARTi5_to_be_MODIfied_ENDaqsdofuha23uru| ²23i ii3uhfia
    oawpo3<9"§ A hSTART4l5o_need5_MODIfic4tion_ENDqa 032/a237(°1Q"§ >A_this_
    START ch4ngeme ENDnot_this_modias
    
    
    • 2
  6. Ed Morton
    2022-01-13T17:57:41+08:002022-01-13T17:57:41+08:00

    在每个 Unix 机器上的任何 shell 中使用任何 awk:

    $ cat tst.awk
    match($0,/START.*END/) {
        tgt = substr($0,RSTART+5,RLENGTH-8)
        sub(/_this_/,"",tgt)
        sub(/modi/,"MODI",tgt)
        gsub(/a/,"4",tgt)
        gsub(/s/,"5",tgt)
        $0 = substr($0,1,RSTART+4) tgt substr($0,RSTART+RLENGTH-3)
    }
    { print }
    

    $ awk -f tst.awk file
    aomodi3hriq32| ¶³r 0q93aoiSTARTi5_to_be_MODIfied_ENDaqsdofuha23uru| ²23i ii3uhfia
    oawpo3<9"§ A hSTART4l5o_need5_MODIfic4tion_ENDqa 032/a237(°1Q"§ >A_this_
    START ch4ngeme ENDnot_this_modias
    
    • 2
  7. Olivier Dulac
    2022-01-14T06:59:02+08:002022-01-14T06:59:02+08:00

    我提出了一个解决方案,它也将

    • 仅在 START 和 END 之间工作,无论介于两者之间(但仅当两者之间没有 START 或 END 时)
    • 即使中间跨越多行也有效

    约束:我假设您的文件不使用 4 个字符,我选择了“经常使用”“\001”到“\004”(但可以使用任何其他未使用的 4 个字符)

    (因为我使用:\001 使任何 END 以换行符开头,任何 END 以换行符结尾,强制“START(nonSTARTnorEND)END”以外的任何其他组合位于单独的行上,因此不考虑。我使用 \ 004 来“保存”原始文件换行符并在最后恢复它们。我使用 \002 代表一个 START,\003 代表一个 END,让我可以检查两者之间是否也没有这些(并且当我查找要替换的字符串时,我以 START 开头并以 END 结尾)。由于这些替换,所有这些都是允许的。

    可以这样做:

    sed -e "s/START/$(printf '\001\002')/g" -e "s/END/$(printf '\003\001')/g" INPUT \
    | tr '\001\n' '\n\004' \
    | gawk '
      /^\002[^\002\003]*\003$/ {
        # we know we are STICTLY between a START(\002) and an END(\003), with none of them occuring inside
        gensub("_this_", "", "g", $0) # remove inbetween START&END all occ. of _this_
        gensub("a", "4", "g", $0) ; gensub("s", "5", "g", $0) # "a" -> "4", "s" -> "5"
        gensub("modi", "MODI", "g", $0)
      }
      1 # print every lines
     ' \
    | tr '\n\004' '\001\n' \
    | tr -d '\001' \
    | sed -e "s/$(printf '\002')/START/g" -e "s/$(printf '\003')/END/g" > OUTPUT
    

    注意:这可以进一步简化(不需要用 \002 替换 START 也不需要用 \003 结束,我首先这样做是为了也能够使用 :[^\002\003]*来确保中间字符串不包含任何一个,但是 \001-> \n 确保已经...)

    • 1
  8. guest_7
    2022-01-15T21:34:43+08:002022-01-15T21:34:43+08:00

    只要您在 s/// 命令中使用带有 /e 标志的 GNU sed,您就可以做您正在尝试的事情:

    sed -Ee "
      s/'/&\\\\&&/
      s/(.*START)(.*)(END.*)/printf %s '\\1' \"\$(printf '%s\\\\n' '\\2'|sed -e 's:_this_::;s:modi:MODI:;y:as:45:')\" '\\3'/e
    " infile
    

    上面可以分解成函数,让它看起来更干净。在这里,我们定义了帮助函数和变量以消除混乱:

    xform() {
      printf '%s\n' "$1" |
      sed -e '
        s/_this_//
        s/modi/MODI/
        y/as/45/
      '
    }
    
    fx() {
      printf %s "$1" "$(xform "$2")" "$3"
    }
    
    export -f fx xform
    
    bre=$(printf '\\(%s\\)'  '.*START' '.*' 'END.*')
    
    sed -e "
      s/'/&\\\\&&/g
      s/$bre/fx '\\1' '\\2' '\\3'/e
    " infile
    

    使用 Perl,这很自然:

    perl -lpe '
      s{(?<=START)(.*?)(?=END)}
       [
         local $_=$1;
         s/_this_//;
         s/modi/MODI/;
         tr/as/45/r;
       ]e;
    ' infile
    

    或者,POSIXly 我们可以将模式空间划分为 3 个部分,存储在保持中,然后转换中间部分并将它们缝合回去。

    sed -e '
      s/\n.*//;ta
      s/START.*END/\
    &\
    /;h;D;:a
      s/_this_//;s/modi/MODI/;y/as/45/
      G;s/\(.*\)\n\(.*\)\n.*\n/\2\1/
    ' infile
    
    • 0

相关问题

  • 重新排列字母并比较两个单词

  • 在awk中的两行之间减去相同的列

  • 多行文件洗牌

  • 如何更改字符大小写(从小到大,反之亦然)?同时[重复]

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve