【问题标题】:How can I write this sed/bash command in awk or perl (or python, or ...)?如何在 awk 或 perl(或 python,或 ...)中编写此 sed/bash 命令?
【发布时间】:2012-07-17 18:32:06
【问题描述】:

我需要用新值 (N,M) where 替换脚本语言中的 Progress (n,m)Progress label="some text title" (n,m) 实例

N= integer ((n/m) * normal)
M= integer ( normal )

进度语句可以在脚本行的任何位置(更糟糕的是,虽然不是当前脚本,但可以跨行拆分)。

normal是1到255之间的指定数字,nm是浮点数

到目前为止,我的sed 实现如下。它仅适用于 Progress (n,m) 格式,不适用于 Progress label="Title" (n,m) 格式,但它只是简单的坚果

#!/bin/bash
normal=$1; 
file=$2
for n in $(sed -rn '/Progress/s/Progress[ \t]+\(([0-9\. \t]+),([0-9\. \t]+)\).+/\1/p' "$file" )
do 
    m=$(sed -rn "/Progress/s/Progress[ \t]+\(${n},([0-9\. \t]+).+/\1/p" "$file")
    N=$(echo "($normal * $n)/$m" | bc)
    M=$normal
    sed -ri "/Progress/s/Progress[ \t]+\($n,$m\)/Progress ($N,$M)/" "$file"
done

简单地说:这行得通,但是,有没有更好的方法?

我的工具箱里有 sedbash 脚本,没有那么多 perlawk 和我认为这个问题更适合的类似的东西。

编辑示例输入。

Progress label="qt-xx-95" (0, 50) thermal label "qt-xx-95" ramp(slew=.75,sp=95,closed) Progress (20, 50) Pause  5 Progress (25, 50) Pause  5 Progress (30, 50) Pause  5 Progress (35, 50) Pause  5 Progress (40, 50) Pause  5 Progress (45, 50) Pause  5 Progress (50, 50)
Progress label="qt-95-70" (0, 40) thermal label "qt-95-70" hold(sp=70)        Progress (10, 40) Pause  5 Progress (15, 40) Pause  5 Progress (20, 40) Pause  5 Progress (25, 40) Pause  5 

【问题讨论】:

  • 您能提供示例输入和预期输出吗?使用起来会简单得多。
  • 听起来您将来需要一个成熟的解析器,所以也许是时候选择一种编程语言了。我推荐 Python,但这只是我自己。
  • @mugenkenichi 添加了示例输入。
  • @IgnacioVazquez-Abrams 我在 lex/yacc 中编写了脚本解析器;但客户不希望我更改代码。
  • @Jamie 请澄清.. 您有一个可以工作的解析器,他们想要更改输出,但不允许您修改解析器?

标签: perl bash scripting sed awk


【解决方案1】:

awk 有很好的拆分能力,所以它可能是解决这个问题的一个不错的选择。

这是一个适用于所提供输入的解决方案,我们称之为update_m_n_n.awk。在 bash 中像这样运行它:awk -f update_m_n_n.awk -v normal=$NORMAL input_file

#!/usr/bin/awk

BEGIN {
  ORS = RS = "Progress"
  FS = "[)(]"
  if(normal == 0) normal = 10
}

NR == 1 { print }

length > 1 { 
  split($2, A, /, */)
  N = int( normal * A[1] / A[2] )
  M = int( normal )
  sub($2, N ", " M)
  print $0
}

说明

  • ORS = RS = "Progress":在Progress 处拆分部分,并在输出中包含Progress
  • FS = "[)(]":括号中的字段分开。
  • NR == 1 { print }:在第一部分之前插入ORS
  • split($2, A, /, */):假设在 Progress 的出现之间只有括号内的项目,这会将 mn 拆分为 A 数组。
  • sub($2, N ", " M):将新值替换到当前记录中。

【讨论】:

    【解决方案2】:

    这有点脆弱,但似乎可以解决问题?可以使用 perl -pe 将其更改为单行,但我认为这更清楚:

    
    use 5.16.0;
    my $normal = $ARGV[0];
    while(<STDIN>){
            s/Progress +(label=\".+?\")? *( *([0-9. ]+) *, *([0-9. ]+) *)/sprintf("Progress $1 (%d,%d)", int(($2/$3)*$normal),int($normal))/eg;
            print $_;
    
    }

    基本思想是可选地捕获$1中的label子句,并将n和m捕获到$2和$3中。我们使用 perl 的能力,通过提供“e”修饰符,用一段评估的代码替换匹配的字符串。如果 label 子句有任何转义的引号或包含与看起来像 Progress toekn 的字符串匹配的字符串,那么它将会大大失败,因此它并不理想。我同意你在这里需要一个诚实的解析器,尽管你可以修改这个正则表达式来纠正一些明显的缺陷,比如 n 和 m 的弱数字匹配。

    【讨论】:

      【解决方案3】:

      我最初的想法是尝试使用递归替换的sedt 命令),但我怀疑这会卡住。

      perl 代码可能适用于未跨行拆分的语句。对于跨行拆分,编写一个单独的预处理器来连接不同的行也许是有意义的。

      代码将“Progress”语句拆分为单独的行段,应用任何替换规则,然后将这些段重新连接为一行并打印。不匹配的行被简单地打印。匹配的代码使用反向引用并且变得有些不可读。我假设您的“正常”参数可以采用浮动值,因为规范似乎不清楚。

      #!/usr/bin/perl -w
      
      use strict;
      
      die("Wrong arguments") if (@ARGV != 2);
      my ($normal, $file) = @ARGV;
      open(FILE, '<', $file) or die("Cannot open $file");
      
      while (<FILE>) {
          chomp();
          my $line = $_;
      
          # Match on lines containing "Progress"
          if (/Progress/) {
      
              $line =~ s/(Progress)/\n$1/go;    # Insert newlines on which to split
              my @segs = split(/\n/, $line);    # Split line into segments containing possibly one "Progress" clause
      
              # Apply text-modification rules
              @segs = map {
                  if (/(Progress[\s\(]+)([0-9\.]+)([\s,]+)([0-9\.]+)(.*)/) {
                      my $newN = int($2/$4 * $normal);
                      my $newM = int($normal);
                      $1 . $newN . $3 . $newM . $5;
                  } elsif (/(Progress\s+label="[^"]+"[\s\(]+)([0-9\.]+)([\s,]+)([0-9\.]+)(.*)/) {
                      my $newN = int($2/$4 * $normal);
                      my $newM = int($normal);
                      $1 . $newN . $3 . $newM . $5;
                  } else {
                      $_;    # Segment doesn't contain "Progress"
                  }
              } @segs;
      
              $line = join("", @segs);    # Reconstruct the single line
          }
      
          print($line,"\n");    # Print all lines
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-11-30
        • 1970-01-01
        • 2012-01-24
        • 2019-03-15
        • 1970-01-01
        • 2017-04-23
        • 2019-02-20
        • 2016-11-22
        相关资源
        最近更新 更多