【问题标题】:Bash: extract text in between two characters which appears more than onceBash:提取出现多次的两个字符之间的文本
【发布时间】:2020-09-16 06:07:16
【问题描述】:

我有一个文件,其中包含三元组形式的图的节点和边。每行有 3 个三元组,但有时中间或末尾有文本:

<samplenode> <sampleEdge> <samplenode>
<samplenode> sometimestheristextinbetween<sampleEdge> <samplenode> and sometimes more at the end
<samplenode> <samplereEdge> <samplenode>

我需要一个只打印三元组并忽略中间文本的命令。所以它应该只包含&lt;&gt;之间的字符

可以包含&lt;&gt;,也可以不包含。没关系,但应该分开。结果可能如下所示:

<samplenode> <sampleEdge> <samplenode>
<samplenode> <sampleEdge> <samplenode> 
<samplenode> <sampleEdge> <samplenode>

我用sed 尝试过,删除了两个模式之间的所有内容(&gt;&lt; 之间的所有内容),但它从来没有按照我想要的方式工作。

有人可以帮我解决吗?也许是grepawk

问候

【问题讨论】:

  • 行尾可以留一个空格吗?
  • &lt;&gt; 可以出现在任何其他上下文中,例如在sometimestheristextinbetween 文本中?
  • @PaulHodges 是的,没关系。
  • @EdMorton 不,它们不会出现在任何其他上下文中

标签: bash awk sed grep


【解决方案1】:

这是一些使用分隔符的 awk 游戏,使用示例输入进行测试。

awk -v RS="<" -F">" '{printf $1 (NR%3==1? "\n": " ")}' file

samplenode sampleEdge samplenode
samplenode sampleEdge samplenode
samplenode samplereEdge samplenode

【讨论】:

    【解决方案2】:

    以下看起来就足够了:

    sed 's/[^>]*\(<[^>]*>\)[^<]*/\1 /g'
    

    【讨论】:

      【解决方案3】:

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

      sed -E 's/^.*(<[^<>]*>).*(<[^<>]*>).*\1.*$/\1 \2 \1/' file
      

      三元组的模式匹配(第一个也是第三个)并仅替换分隔的匹配空间。

      【讨论】:

        【解决方案4】:

        sed 的另一个变体。

        sed -E 's/>[^<]*(<*)/> \1/g'
        

        这匹配从结束标记到另一个的打开(或行尾),并替换为关闭、空格和任何与另一个打开的测试匹配的内容(在 EOL 时为空)。

        使用对您更有意义的阅读方式。

        如果您不想使用-Extended 模式匹配,那么

        sed 's/>[^<]*\(<*\)/> \1/g'
        

        如果您不想要 EOL 的空间,请添加修剪。

        sed -E 's/>[^<]*(<*)/> \1/g; s/ $//;'
        

        【讨论】:

        • 非 GNU 可能不允许使用分号,但您可以用换行符替换它们。
        【解决方案5】:

        我会采用 awk 的方法,您能否尝试以下应该适用于任何类型的 awk

        awk '
        {
          val=""
          while(match($0,/<[^>]*/)){
            val=(val?val OFS:"")substr($0,RSTART,RLENGTH+1)
            $0=substr($0,RSTART+RLENGTH+1)
          }
          print val
        }' Input_file
        

        说明:为上述添加详细说明。

        awk '                                                      ##Starting awk program from here.
        {
          val=""                                                   ##Nullifying val here.
          while(match($0,/<[^>]*/)){                               ##Running whole loop and mentioning match inside it to match everything from < till very first occurence of > in current line.
            val=(val?val OFS:"")substr($0,RSTART,RLENGTH+1)        ##Creating val which has sub-string of matched part here.
            $0=substr($0,RSTART+RLENGTH+1)                         ##Re-creating current line where already matched part is removed.
          }
          print val                                                ##Printing val here.
        }' Input_file                                              ##Mentioning Input_file name here.
        

        【讨论】:

          【解决方案6】:

          另一个awk

          $ awk 'BEGIN {b="<";e=">";FS="["b e"]"} 
                       {for(i=2;i<=NF;i+=2) printf "%s ", b $i e; print ""}' file
          
          <samplenode> <sampleEdge> <samplenode>
          <samplenode> <sampleEdge> <samplenode>
          <samplenode> <samplereEdge> <samplenode>
          

          【讨论】:

            【解决方案7】:

            grep -o 将只打印该行的匹配部分(每行输出一个匹配),然后您可以使用paste 将结果列成三列。这取决于每行恰好有三个匹配项。

            $ grep -o '<[^>]*>' file | paste - - -
            <samplenode>        <sampleEdge>        <samplenode>
            <samplenode>        <sampleEdge>        <samplenode>
            <samplenode>        <samplereEdge>      <samplenode>
            

            【讨论】:

            • 如果你只想要一个空格分隔符,添加-s\ -s' '
            • 这行得通,但如果&lt;&gt; 之间有超过 23 个字符,它将删除第 23 个字符之后的所有字符。
            • 谢谢@SBKIT,对于这个用例来说,这根本不是pr 的一个很好的功能。更新为使用 paste
            【解决方案8】:

            对于 FPAT 使用 GNU awk 并假设 &lt;&gt; 均未出现在任何其他上下文中:

            $ awk -v FPAT='<[^>]+>' '{$1=$1}1' file
            <samplenode> <sampleEdge> <samplenode>
            <samplenode> <sampleEdge> <samplenode>
            <samplenode> <samplereEdge> <samplenode>
            

            【讨论】:

              猜你喜欢
              • 2020-10-15
              • 1970-01-01
              • 2018-11-02
              • 1970-01-01
              • 2019-08-01
              • 2012-07-07
              • 1970-01-01
              • 2019-07-02
              • 1970-01-01
              相关资源
              最近更新 更多