如何使用sed、bash或awk替换字符串中最后出现的-?

如何使用sed、bash或awk替换字符串中最后出现的-?,bash,awk,sed,Bash,Awk,Sed,输入文件示例: xxx-xxx(-) xxx xxx xxx - 2e-15 Cytochrome b-c1 complex subunit 9 xxx xxx:241-77(-) xxx-xxx(+) xxx xxx xxx + 3e-24 Probable endo-beta-1,4-glucanase D xxx xxx:241-77(+) 我一直在尝试sed,但没

输入文件示例:

xxx-xxx(-)        xxx   xxx  xxx      -       2e-15   Cytochrome b-c1 complex subunit 9       xxx   xxx:241-77(-)
xxx-xxx(+)        xxx   xxx  xxx      +       3e-24   Probable endo-beta-1,4-glucanase D       xxx   xxx:241-77(+)
我一直在尝试sed,但没有成功。我可以看出以下两件事是有效的:

rev file|sed -e 's/-/M/'|rev
rev file|sed -e 's/)/M/'|rev
但是,
-
一起不起作用:

rev file|sed -e 's/-)/M/'|rev

这是因为
rev
颠倒了顺序,你知道吗<代码>-)在反向版本中不会出现;它位于反向文件中的
)-

rev file|sed -e 's/)-/M/'|rev

这是因为
rev
颠倒了顺序,你知道吗<代码>-)在反向版本中不会出现;它位于反向文件中的
)-

rev file|sed -e 's/)-/M/'|rev
用纯(GNU)sed“替换某物最后一个的第n个”的一般方法
  • 我们想用输入中其他地方找不到的独特内容(例如
    ~B
    )替换“something”(在本例中为
    -)
    )。为了确保输入中没有此序列,我们首先将所有
    ~
    替换为
    ~A

    sed 's/~/~A/g' infile
    
  • 将所有“某物”(在本例中为
    -)
    )替换为
    ~B
    ,我们现在知道它是唯一的:

    sed 's/-)/~B/g'
    
    现在,您的输入文件如下所示(略微编辑,使其适合此处的线宽):

  • 下一个命令执行此操作:“只要行有n+1个
    ~B
    ,就用
    -)
    替换第一个。
    :a
    ta
    是要分支到的标签和条件分支(“如果发生替换,则转到标签
    :a
    ):

    对于n=1的情况,即我们希望替换最后出现的情况,当然不需要量词
    \{1\}
    ,但可以替换为n的其他值

    输入文件现在有一个唯一的
    ~B
    ,其中最后一个
    -)
    过去是:

    xxx-xxx(-)  xxx   -   2e-15   Cytochrome b-c1 complex subunit 9   xxx   xxx:241-77(~B
    xxx-xxx(+)  xxx   +   3e-24   Probable endo-beta-1,4-glucanase D   xxx   xxx:241-77(+)
    
  • 我们将替换单个
    ~B

    sed 's/~B/M/'
    
    导致

    xxx-xxx(-)  xxx   -   2e-15   Cytochrome b-c1 complex subunit 9   xxx   xxx:241-77(M
    xxx-xxx(+)  xxx   +   3e-24   Probable endo-beta-1,4-glucanase D   xxx   xxx:241-77(+)
    
  • 现在可以将
    ~B
    的其余部分替换为原来的
    -)
    (本例中没有操作):

  • 最后,我们撤销第一次替换(这对本例没有影响,因为输入没有
    ~
    开始):

  • 全部在一行中:

    sed 's/~/~A/g;s/-)/~B/g;:a;/~B\(.*~B\)\{1\}/s/~B/-)/;ta;s/~B/M/;s/~B/-)/g;s/~A/~/g' infile
    
    或者,为了可读性,多行:

    sed '
    s/~/~A/g
    s/-)/~B/g
    :label
    /~B\(.*~B\)\{1\}/s/~B/-)/
    t label
    s/~B/M/
    s/~B/-)/g
    s/~A/~/g
    ' infile
    
    当然,对于n=1的情况,有更简单的解决方案,例如。

    用纯(GNU)sed“替换某物最后一个的第n个”的一般方法
  • 我们想用输入中其他地方找不到的唯一内容(例如
    ~B
    )替换“something”,在本例中为
    -)
    。为了确保此序列不在输入中,我们首先将所有
    ~
    替换为
    ~A

    sed 's/~/~A/g' infile
    
  • 将所有“某物”(在本例中为
    -)
    )替换为
    ~B
    ,我们现在知道它是唯一的:

    sed 's/-)/~B/g'
    
    现在,您的输入文件如下所示(略微编辑,使其适合此处的线宽):

  • 下一个命令执行此操作:“只要行有n+1个
    ~B
    ,就用
    -)
    替换第一个。
    :a
    ta
    是要分支到的标签和条件分支(“如果发生替换,则转到标签
    :a
    ):

    对于n=1的情况,即我们希望替换最后出现的情况,当然不需要量词
    \{1\}
    ,但可以替换为n的其他值

    输入文件现在有一个唯一的
    ~B
    ,其中最后一个
    -)
    过去是:

    xxx-xxx(-)  xxx   -   2e-15   Cytochrome b-c1 complex subunit 9   xxx   xxx:241-77(~B
    xxx-xxx(+)  xxx   +   3e-24   Probable endo-beta-1,4-glucanase D   xxx   xxx:241-77(+)
    
  • 我们将替换单个
    ~B

    sed 's/~B/M/'
    
    导致

    xxx-xxx(-)  xxx   -   2e-15   Cytochrome b-c1 complex subunit 9   xxx   xxx:241-77(M
    xxx-xxx(+)  xxx   +   3e-24   Probable endo-beta-1,4-glucanase D   xxx   xxx:241-77(+)
    
  • 现在可以将
    ~B
    的其余部分替换为原来的
    -)
    (本例中没有操作):

  • 最后,我们撤销第一次替换(这对本例没有影响,因为输入没有
    ~
    开始):

  • 全部在一行中:

    sed 's/~/~A/g;s/-)/~B/g;:a;/~B\(.*~B\)\{1\}/s/~B/-)/;ta;s/~B/M/;s/~B/-)/g;s/~A/~/g' infile
    
    或者,为了可读性,多行:

    sed '
    s/~/~A/g
    s/-)/~B/g
    :label
    /~B\(.*~B\)\{1\}/s/~B/-)/
    t label
    s/~B/M/
    s/~B/-)/g
    s/~A/~/g
    ' infile
    

    当然,对于n=1的情况,有更简单的解决方案,例如。

    您不需要多个具有管道链或奇特操作的命令-因为seds regexp是贪婪的,所以您只需要:

    $ sed 's/\(.*\)-)/\1M/' file
    xxx-xxx(-)        xxx   xxx  xxx      -       2e-15   Cytochrome b-c1 complex subunit 9       xxx   xxx:241-77(M
    xxx-xxx(+)        xxx   xxx  xxx      +       3e-24   Probable endo-beta-1,4-glucanase D       xxx   xxx:241-77(+)
    

    您不需要具有管道链或奇特操作的多个命令-因为seds regexp是贪婪的,所以您只需要:

    $ sed 's/\(.*\)-)/\1M/' file
    xxx-xxx(-)        xxx   xxx  xxx      -       2e-15   Cytochrome b-c1 complex subunit 9       xxx   xxx:241-77(M
    xxx-xxx(+)        xxx   xxx  xxx      +       3e-24   Probable endo-beta-1,4-glucanase D       xxx   xxx:241-77(+)
    

    仅供参考,您可以创建这样一个字符串:
    sed's/~/~a/g;s/-)/~B/g;s/~B/-)/g;s/~A/~/g'
    。第一种替代方法确保输入文件中的每个
    ~
    现在都被
    A
    取代,这样字符串
    ~B
    (而不是你的
    ~
    ——如果你重复一个字符,我不相信这种方法有效)现在无法出现在输入文件中,在您对其执行所有操作后,最后一次替换将再次展开。不过,我不明白为什么需要这样做-您正在将一个2字符字符串
    -)
    更改为另一个2字符字符串
    ~
    -为什么不跳过它,然后对其执行所有后续操作
    -)
    而不是
    ~
    ?@EdMorton你的答案当然要优雅得多。首先将字符串更改为另一个字符串的原因是将除最后一个字符串外的所有字符串都更改为原始字符串,因此我们有了
    ~
    来替换原始字符串-我不知道如何直接在原始字符串上执行此操作。@EdMorton添加了保证唯一性的内容。这整个答案现在看起来相当愚蠢,看它能做得多么简单……仅供参考,与其尝试猜测文件中不存在的2个字符的字符串,不如创建一个这样的字符串:
    sed's/~/~a/g;s/-)/~B/g;s/~B/-)/g;s/~A/~/g'
    。第一种替代方法确保输入文件中的每一个
    ~
    现在都被
    A
    取代,这样字符串
    ~B
    (而不是你的
    ~
    ——我不相信如果你重复一个字符,这种方法会起作用)现在就不能出现在输入文件和最后一个子文件中