I need to replace instances of Progress (n,m) and Progress label="some text title" (n,m) in a scripting language with new values (N,M) where
N= integer ((n/m) * normal)
M= integer ( normal )
The progress statement can be anywhere on the script line (and worse, though not with current scripts, split across lines).
The value normal is a specified number between 1 and 255, and n and m are floating point numbers
So far, my sed implementation is below. It only works on Progress (n,m) formats and not Progress label="Title" (n,m) formats, but its just plain nuts:
#!/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
Simply put: This works, but, is there a better way?
My toolbox has sed and bash scripting in it, and not so much perl, awk and the like which I think this problem is more suited to.
Edit Sample input.
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
awk has good splitting capabilities, so it might be a good choice for this problem.
Here is a solution that works for the supplied input, let's call it update_m_n_n.awk. Run it like this in 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": Split sections at Progress and include Progress in the output.FS = "[)(]": Separate fields at parenthesis.NR == 1 { print }: Insert ORS before the first section.split($2, A, /, */): Assuming there is only on parenthesized item between occurrences of Progress, this splits m and n into the A array.sub($2, N ", " M): Substitute the new values the into current record.This is somewhat brittle but it seems to do the trick? It could be changed to a one-line with perl -pe but I think this is clearer:
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 $_;
}
The basic idea is to optionally capture the label clause in $1, and to capture n and m into $2 and $3. We use perl's ability to replace the matched string with an evaluated piece of code by providing the "e" modifier. It's going to fail dramatically if the label clause has any escaped quotes or contains the string that matches something that looks like a Progress toekn, so its not ideal. I agree that you need an honest to goodness parser here, though you could modify this regex to correct some of the obvious deficiencies like the weak number matching for n and m.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With