Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: Converting bash script to zsh for converting bytes to 'readable'



TJ Luoma wrote on Wed, Sep 12, 2018 at 18:02:26 -0400:
> I don't really understand what the 'while' loop is doing, and I don't know
> if zsh has 'floats' (which I assume is 'floating point'?).
> 

Yes, zsh has floating point variables, for example, 'typeset -F foo'.

The 'while' loop divides the number by 1000 in each iteration to figure
out the magnitude.  It's like counting digits to figure out the exponent
to use for scientific notation (1024 == 1.024e+3).

> METRIC=('KB' 'MB' 'GB' 'TB' 'XB' 'PB')

This should read KB MB GB TB PB EB ZB YB.

> Does that look correct to you? Did I miss anything? It appears to work in
> my (limited, so far) testing, but I'm worried that I'm overlooking
> something.
> 

It may be correct, but it's not idiomatic.  For one, it's not idiomatic
to treat floating point numbers as strings.  I'm not sure what scale=2
to bc(1) does but it's probably possible to do that in native zsh too.
Likewise for the tr(1) though that's not as crucial.

For instance, the loop could have been written like this:
.
    while (( UNITS <= MBYTES )); do
.
(but read on)

> Alternatively: is there another / easier way of doing this that I should
> use instead? Some 'zmodload' or something?

I think this does what you want?

     1	zmodload zsh/mathfunc
     2	f() {
     3	  setopt localoptions ksharrays
     4	  integer i=$(( log2( $1 )/10 )) 
     5	  local -a SI=( '' 'Ki' 'Mi' 'Gi' )
     6	  printf '%5.2g %s\n' "$(( ($1)*1.0 / (1<<(10*i)) ))" "${SI[i]}B"
     7	}

Explanation (by line number)

1: Enables the 'log2' math function (requires zsh 5.6)

3: I'd like to use zero-based array indexes, purely for convenience.
(I could've changed [i] to [i+1] on line 7 instead.)

4: Take the base-2 logarithm of the input and divide it by 10 to get the
order of magnitude.  Using an integer variable implicitly truncates the
input.  (You may prefer rounding to truncating, in order for 1023 to map
to KiB rather than to plain B.)

6: Divide the input by the largest power of 1024 smaller than it, printf
that, and append the appropriate SI prefix from the lookup array.
Multiply by 1.0 to ensure at least one of the arguments to '/' is a
float, in order for the division expression to evaluate to a floating
point quantity.  (Integer division results in an integer: compare
$((5/2)) and $((5.0/2)).)

Cheers,

Daniel



Messages sorted by: Reverse Date, Date, Thread, Author