我需要使用新值Progress (n,m)
替换脚本语言中Progress label="some text title" (n,m)
和(N,M)
的实例
N= integer ((n/m) * normal)
M= integer ( normal )
进度语句可以在脚本行的任何位置(更糟糕的是,虽然不是当前脚本,但是跨行分割)。
值normal
是1到255之间的指定数字,n
和m
是浮点数
到目前为止,我的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
简单地说:这有效,但有更好的方法吗?
我的工具箱中包含sed
和bash
脚本,而不是perl
,awk
等,我认为这个问题更适合。< / p>
编辑示例输入。
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
答案 0 :(得分: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
出现之间只有带括号的项目,这会将m
和n
拆分为A
数组。sub($2, N ", " M)
:将新值替换为当前记录。答案 1 :(得分:1)
这有些脆弱,但似乎可以解决问题?它可以改为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的弱数匹配。
答案 2 :(得分:0)
我最初的想法是尝试sed
使用递归替换(t
命令),但我怀疑它会卡住。
此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
}