0

I'm looking for a simpler way to express this expression and if statement, without the repetition and also without using awk

stat="0.92" # hardcoded for demonstration purposes

cutoff_percent=90

if awk "BEGIN {exit !($stat > $cutoff_percent / 100 )}"
then
    echo "Passed: test coverage  is above the $cutoff_percent% cutoff."
else
    echo "Failure: test coverage isn't above the $cutoff_percent% cutoff."
fi

This is what the output is:

Passed: test coverage is above the 90% cutoff.

Bonus: Can you simplify it so I don't have to install anything over a basic Alpine Linux OS container image?

Preet Sangha
  • 64,563
  • 18
  • 145
  • 216
  • 2
    Please, never show text with images. They are not searchable, not copy-paste-able and much heavier than needed. Moreover they affect accessibility negatively. Please copy-paste the text in your question and [format it properly](https://stackoverflow.com/help/formatting), instead. – Renaud Pacalet Aug 02 '23 at 08:35
  • I have removed it. Thanks for the reference. – Preet Sangha Aug 02 '23 at 08:41
  • 3
    What is the meaning of _Bonus: Can you simple_ ? – user1934428 Aug 02 '23 at 09:08
  • 1
    _without using awk_ : Well, the majority of programming languages can used for this. If you hate `awk`, suitable languages would be `bc`, `zsh`, `Perl` `Ruby` and many more. I will post a solution for Ruby, but it would make more sense if you would pick a language you don't despise. Just saying "everything else but awk" is a bit broad. – user1934428 Aug 02 '23 at 09:13
  • 3
    What's wrong with awk? – Paolo Aug 02 '23 at 11:28
  • @user1934428 - Corrected the cut-off text. I am keeping the options open - but I'd prefer not to have anything that means I'd have to install on a basic Alpine Linux container. – Preet Sangha Aug 02 '23 at 19:43
  • 1
    awk is a POSIX-specified standard tool. You shouldn't need to install it at all. (And more to the point, awk has built-in floating-point arithmetic, which standard shells do not; it's thus why folks writing POSIX shell often/frequently cut over to awk for math, and why it's considered good practice to do so). – Charles Duffy Aug 02 '23 at 21:50
  • @CharlesDuffy : _which standard shells do not_ .... Wouldn't you consider zsh a standard shell by now? – user1934428 Aug 03 '23 at 06:33
  • @user1934428, "Standard" means complying with POSIX.2; it has nothing to do with popularity. zsh's team explicitly chooses to break the standard when they think it enforces bad decisions. I think they're right that POSIX.2 enforces bad decisions that were made decades ago, but I also think they're wrong to not comply; it breeds a generation of users who don't know how to write scripts that work correctly on any shell but zsh because they're accustomed to being able to ignore quoting rules and the like. It would be better if they followed fish, elvish or pwsh and didn't even _look- POSIXy. – Charles Duffy Aug 03 '23 at 11:30
  • @CharlesDuffy : My misunderstanding: When you said "standard shell", I didn't understood it in the sense of POSIX standard. I thought that you are talking about shells which we usually can expect to be present in an operating system. Of course floating point arithmetic is not present in a POSIX standard shell. Reason for misunderstanding was the comment of the OP, that he does not want a solution which he would have to install in his container. – user1934428 Aug 03 '23 at 13:02
  • @user1934428, even in that context: Alpine does not come with zsh. – Charles Duffy Aug 03 '23 at 14:09
  • 1
    I wonder if this is an XY problem. Let me explain... because the shell _usually_ does not perform floating point calculations, `stat` is possibly set using the output of an external program/script and maybe it's simpler and more maintainable to modify it adding an option, say `-int`, to have less problems downstream – gboffi Aug 03 '23 at 19:30
  • yes, that might be how have to solve it. Thank you – Preet Sangha Aug 23 '23 at 02:39

6 Answers6

2

If you can use bc, instead of awk (output prefixed with -|):

stat="0.92"; cutoff_percent=90
str1="Passed: test coverage is above the $cutoff_percent% cutoff.\n"
str2="Failure: test coverage isn't above the $cutoff_percent% cutoff.\n"
bc <<< "scale = 20; if($stat > $cutoff_percent / 100) print \"$str1\" else print \"$str2\""
-| Passed: test coverage is above the 90% cutoff.

Tune the scale value (number of digits right of the decimal point) to the desired precision.

If you cannot use bc but stat is always a 0.xx number with 2 digits right of the decimal point and cutoff_percent an integer:

if (( ${stat#0.} > cutoff_percent )); then echo "$str1"; else echo "$str2"; fi
-| Passed: test coverage is above the 90% cutoff.

If stat is always a 0.xxx number but with any number of digits right of the decimal point:

stat="0.9256"
tmp="${stat:4}"; tmp="${tmp//?/0}"
if (( ${stat#0.} > cutoff_percent$tmp )); then echo "$str1"; else echo "$str2"; fi
-| Passed: test coverage is above the 90% cutoff.
Renaud Pacalet
  • 25,260
  • 3
  • 34
  • 51
2

IFF stat is ALWAYS written as 0.xy, that is zero dot two digits, then you can strip the "0." using parameter expansion.

$ cutoff_percent=90 ; for stat in 0.89 0.90 0.91 ; do
> [ ${stat/0.} -ge $cutoff_percent ] && echo not less || echo less
> done
less
not less
not less
$

Addendum


You can always use fixed point arithmetic

$ fixed () {
# convert a floating point number to millionths
i=${1/.*/}
j=${i:-0}
if [ $1 == $j ] ; then # no decimal point
   d=0
else
   d=${1/"$i"./}
fi
d=${d}000000
d=${d:0:6}
if [ $j -ge 0 ] ; then
   # https://stackoverflow.com/a/11130324/2749397
   echo $(( $j*1000000 + $((10#$d)) ))
else
   echo $(( $j*1000000 - $((10#$d)) ))
fi
}
$ alias f=fixed
$ f 2; f -2; f .465; f .0465 ;f 3.12 ;f -2.45 ; f +3.0045 ;f -3.0045 ;f 3.1234567
2000000
-2000000
465000
46500
3120000
-2450000
3004500
-3004500
3123456
$
gboffi
  • 22,939
  • 8
  • 54
  • 85
2

perl:

export stat cutoff_percent
perl -e '
    $ok = ($ENV{stat} > $ENV{cutoff_percent} / 100) ? 0 : 1;
    printf "%s: test coverage %s above the %d cutoff.\n", 
        ("Passed", "Failure")[$ok],
        ("is", "isn\047t")[$ok], 
        $ENV{cutoff_percent};
    exit $ok;
'
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
1

Since you said "any language except awk", I propose here a Ruby solution:

if ruby -e "exit(100*$stat > $cutoff_percent ? 0 : 1)"
then
  ...
fi
user1934428
  • 19,864
  • 7
  • 42
  • 87
1

@Preet Sanghacut : what's wrong this ultra clean approach

off_percent=90
jot -w '%.5f' 20 0.05319 - 0.08469 | 

mawk '$++_*(_!_--_)<+__' __="$cutoff_percent"

0.05319
0.13788
0.22257
0.30726
0.39195
0.47664
0.56133
0.64602
0.73071
0.81540
RARE Kpop Manifesto
  • 2,453
  • 3
  • 11
  • Would you mind describing your `mawk` a bit? I understand that `__` is the variable holding the cutoff. I guess that `_` is somehow related to the input line, but what are the `$++_` and `--` and `!` for instance doing? – user1934428 Aug 03 '23 at 06:27
  • @user1934428 : as for what `mawk` is - it's just my preferred `awk` parser - trading the bells and whistles of `gawk` for sizable speed gains in `mawk`. – RARE Kpop Manifesto Aug 07 '23 at 11:27
  • @user1934428 : in `awk`, any conjoining items that cannot form a variable name automatically becomes a string concat. `$++_` makes `_` a `1`. `_!_` is a string concat because it got by logical negation `!`, so 1 concat with in logically negated of 1, = `"10"`, and since 2 minus signs cannot form a proper subtraction ("subtracting" the "negated value" is just a stupid way of saying "add the value"), it's parsed as `(_ !_-- _)`, which becomes `100` – RARE Kpop Manifesto Aug 07 '23 at 11:30
  • _[“Though this be madness, yet there is method in 't.”](https://www.allgreatquotes.com/hamlet-quotes-121/)_ .... I'm speechless. For the benifit of others, would you mind putting this explanation into the answer, instead of just leaving it as a comment? – user1934428 Aug 07 '23 at 11:56
  • @user1934428 : i mean within that tiny bit of code I've already showcased pre-increment, post-decrement, no-frills multiplication, logical negation, seamless string conversion via `CONVFMT`, gapless string concat, grouping, field referencing, forced numeric comparison, command line parameter assignment, demonstrated that `_` isn't a special/reserved variable at all, and leveraged the default `{ print }` action…. and that's already without calling any functions or fudging the row/column delimiters – RARE Kpop Manifesto Aug 10 '23 at 04:25
1

You can't rely on the shell to have floating-point arithmetic, so use scaled arithmetic:

scale=1000
stat=920
cutoff_percent=90

if [ $((stat * 100 > cutoff_percent * scale)) -ne 0 ] ; then
   echo passed
else
   echo failed
fi

If need be, values like 0.920 can be scaled textually by moving the decimal point. To do this, we can use a function like this one:

scale_digits=3
scale=1000

scale()
{
  local norm=$(printf "%.*f" $scale_digits "$1")
  local int=${norm%.*}
  local frac=${norm#*.}

  case $int in
    0 )
      printf "%s\n" $(( 1$frac - $scale ))
      ;;
    -0 )
      printf "%s\n" $(( $scale - 1$frac ))
      ;;
    * )
      printf "%s\n" $int$frac
      ;;
   esac
}

Examples:

$ scale 0.1
100
$ scale 0
0
$ scale 000
0
$ scale +0
0
$ scale -0
0
$ scale -00
0
$ scale 0.1
100
$ scale .1
100
$ scale -.1
-100
$ scale -.12
-120
$ scale -.001
-1
$ scale +.001
1
$ scale +0.001
1
$ scale 0.001
1
$ scale -0.001
-1
$ scale -0.001223
-1
$ scale 0.001223
1
$ scale 123.456
123456
$ scale 123.4567
123457
$ scale 1.0e3
1000000
$ scale 1.0e2
100000
$ scale 1.0e0
1000
$ scale 1.0e-1
100
$ scale 1.0e-2
10
$ scale 1.0e-3
1

I'm not using anything but POSIX shell syntax and the printf utility. I tested with Dash and Bash. If your container has any POSIX shell and printf, this should all work.

Kaz
  • 55,781
  • 9
  • 100
  • 149