【问题标题】:Get the character that precede each occurrence of given character/pattern in a string获取字符串中每次出现给定字符/模式之前的字符
【发布时间】:2019-06-29 12:25:56
【问题描述】:

我正在尝试使用标准 bash 工具(如 grep、awk/gawk、sed ...)获取字符串中每次出现给定字符/模式之前的字符。

Step I:获取每次出现:之前的字符

例子:

字符串 1 => :hd:fg:kl:

字符串 2 => :df:lkjh:

字符串 3 => :glki:l:s:d:

预期结果

结果 1 => dgl

结果 2 => fh

结果 3 => ilsd

我用 awk 尝试了很多次,但都没有成功

Step II: 在结果字符串的每个字符之间插入一个给定的字符

/ 为例

结果 1 => d/g/l

结果 2 => f/h

结果 3 => i/l/s/d

这一步我有一个 awk 表达式awk -F '' -v OFS="/" '{$1=$1;print}'

我不知道是否可以使用 awk 或 sed 执行 Step I 以及为什么不一次执行 Step IStep II

亲切的问候

【问题讨论】:

  • 你可能会发现这个问题很有帮助stackoverflow.com/questions/2777579/…
  • @moocan,请尝试选择任何答案作为正确答案并完全关闭问题,也请参阅此链接以获取更多详细信息stackoverflow.com/help/someone-answers
  • 如果可能发生这种情况,您应该在示例输入/输出中包含一个背靠背冒号的情况(例如 foo::bar),因为根据您的要求,这可能难以处理所以。输出是o 还是o: 还是别的什么?如果它不能发生,那么在您的问题中添加一个声明。
  • @RavinderSingh13 很抱歉我的回复迟了,因为我离线了
  • @Ed Morton,这在我的情况下不会发生......但这是一个非常好的建议

标签: bash awk sed


【解决方案1】:

怎么样:

awk 'BEGIN{FS=":"}{for(i=1;i<NF;i++){if(i>2)printf"/";printf substr($i,length($i))}print""}' input.txt

输入.txt:

:hd:fg:kl:
:df:lkjh:
:glki:l:s:d:

输出:

d/g/l
f/h
i/l/s/d

【讨论】:

    【解决方案2】:

    解决方案 1:您能否尝试关注,如果这对您有帮助,请告诉我。

    awk -F":" '
    {
      for(i=1;i<=NF;i++){
        if($i){ val=(val?val:"")substr($i,length($i)) }
      }
      print val;
      val=""
    }' Input_file
    

    输出如下。

    dgl
    fh
    ilsd
    

    解决方案 2: 在输出字符串之间使用 /

    awk '
    BEGIN{
      OFS="/";
      FS=":"
    }
    {
      for(i=1;i<=NF;i++){
        if($i){
          val=(val?val OFS:"")substr($i,length($i))
        }}
      print val;
      val=""
    }' Input_file
    

    输出如下。

    d/g/l
    f/h
    i/l/s/d
    

    解决方案 3: 使用 match 实用程序 awk

    awk '
    {
      while(match($0,/[a-zA-Z]:/)){
        val=(val?val:"")substr($0,RSTART,RLENGTH-1)
        $0=substr($0,RSTART+RLENGTH)
       }
      print val
      val=""
    }'  Input_file
    

    【讨论】:

    • 在我的问题中,我总是以“:”结束我的示例,这是我的一个错误,因为它也可以以任何字母结尾。对于“:hfd:l:jh:m”等模式,第一个解决方案的输出是“dlhm”,第二个解决方案的输出是“d/l/h/m”。您的第三个解决方案效果很好,因为输出是“dlh”。
    • @moocan, sue 感谢您告诉我,请尽量按照您的要求保留问题的样本,因为我们会根据您的样本给出解决方案,欢呼雀跃,学习愉快。
    【解决方案3】:

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

    sed -r 's/[^:]*([^:]):+|:+/\1/g;s/\B/\//g' file
    

    将零个或多个非: 后跟单个字符,后跟: 或单个: 全局替换为整个行中的单个字符。然后替换在每个字符之间插入一个/

    【讨论】:

    • 在“:hfd:l:jh:m”等模式的情况下,输出为“d/l/h/m”。在我的问题中,我总是用“:”结束我的例子,这是我的一个错误,因为它也可以以任何字母结尾
    【解决方案4】:

    Perl 和负前瞻:

    $ perl -p -e 's/.(?!:)//g' file
    dgl
    fh
    ilsd
    

    【讨论】:

      【解决方案5】:

      perl 更容易做到这一点

      $ cat ip.txt
      :hd:fg:kl:
      :df:lkjh:
      :glki:l:s:d:
      
      $ perl -lne 'print join "/", /.(?=:)/g' ip.txt
      d/g/l
      f/h
      i/l/s/d
      
      • /.(?=:)/g 获取: 之前的所有字符
      • 然后使用/ 作为分隔符字符串打印结果匹配项

      【讨论】:

      • 适用于我所有的测试模式,即使模式不是以“:”结尾而是任何字母。谢谢
      【解决方案6】:

      所有 sed 和 ERE

      sed -E 's#[^:]*(.):#\1/#g;s/^.|.$//g' infile
      

      【讨论】:

        【解决方案7】:

        使用 GNU sed:

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

        第一个命令,s/[^:]*([^:]):/\1/g 匹配去掉多余的字符和冒号(第一个除外),所以产生这个:

        :dgl
        :fh
        :ilsd
        

        第二个命令s/([^:])/\/\1/g 在每个字符前插入一个/,产生:

        :/d/g/l
        :/f/h
        :/i/l/s/d
        

        最后一个命令s/^:\/// 只是从每行的开头删除:/

        d/g/l
        f/h
        i/l/s/d
        

        【讨论】:

          【解决方案8】:

          您可以使用 gawk 从第二个字符开始遍历每一行。每次迭代器遇到冒号时打印前一个字符。

          $ awk <file.txt '{for(i=2;i<=length($0);i++) { \
                              if (substr($0,i,1)==":") printf substr($0,i-1,1);} printf "\n";}'
          dgl
          fh
          ilsd
          

          【讨论】:

            猜你喜欢
            • 2021-12-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-01-05
            • 1970-01-01
            • 2022-10-13
            • 2014-07-10
            • 1970-01-01
            相关资源
            最近更新 更多