help-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Calculation


From: Greg Wooledge
Subject: Re: Calculation
Date: Sun, 20 Jun 2021 09:50:47 -0400

On Sun, Jun 20, 2021 at 10:57:16AM +0200, Silvio Siefke wrote:
> Yet I use:
> 
> round_sand=$(concalc "$i"*0.04*1.8 | awk '{print 
> ($0-int($0)<0.499)?int($0):int($0)+1}') 
> 
> $i is the array with meters. With this command I count the sand I need. 

The first problem I see is that you've got unquoted * characters, here:

"$i"*0.04*1.8

This will be expanded as a glob, if possible, and if shell options have
not been altered to explicitly prevent it.  If you happen to have one or
more files which this glob ends up matching, you'll get those filenames
passed into awk, instead of your arithmetic expression.

You can simply move the closing quote to enclose the whole expression,
to prevent this problem:

"$i*0.04*1.8"

> For example 10 tons.Now I calculate the price for sand.
> 
> sand=$(concalc "$round_sand"*8)

The same thing applies here.

> My question is, is there a way to make this two variables in one? 

What do you mean by this?

> I try with xargs but this will not work. 

Do you mean that you want to pass a stream of expressions, one per
line, into an awk program, and let awk emit a stream of answers, one
per line?

If so, then yes, this is certainly possible.  Awk runs in this mode by
default.

The structure of an awk program is as follows:

awk [options] '
  CONDITION1 {ACTION1}
  CONDITION2 {ACTION2}
  ...
' [files]

Each line of input is matched against each CONDITION in turn.  Lines that
match a given CONDITION will execute the corresponding ACTION.

Your awk program is using the empty CONDITION which matches every line,
so it will execute your {ACTION} for every line of input.

So, all you have to do to make your awk program act on each line of
input separately is simply to feed multiple lines of input to it.

send_multiple_lines | awk '{print ($0-int($0)<0.499)?int($0):int($0)+1}'

Now, your next question is probably going to be something like, "How
do I deal with the stream of multiple results?  I did x=$(foo|awk)
but now what?"

The thing is, you don't want to use a command substitution $() here.
Not at all.

You've got a stream of data, so you want to read it line by line.  The
model for that is a while-read loop:

send_multiple_lines |
  awk '{your thing}' |
  while read -r answer; do
    ... whatever ...
  done

Or this variant, if you want the while loop to be run in the current
shell process instead of a subshell:

while read -r answer; do
  ... whatever ...
done < <(send_multiple_lines | awk '{your thing}')

Or, if you need to *store* all of the results in memory at once for
some reason, you could load them into an array:

mapfile -t answers < <(send_multiple_lines | awk '{your thing}')

Then you can iterate over the array however you like.

If your third question is "How do I make concalc emit multiple lines from
multiple inputs", well, we have no idea, because we have no idea what
this "concalc" command is, or how it works.  You'll have to figure that
part out yourself.  You may be able to feed multiple inputs to it at once,
or you may have to run it multiple times in a loop.  The inputs might be
passable as arguments, or as standard input, or either one.  It completely
depends on how the programmers of "concalc" decided to make it work.


P.S. Looking at your awk command and your variable choice one final time,
it looks like you're simply trying to round a floating-point value to
an integer value.  There are easier ways to do that.

unicorn:~$ printf '%.0f\n' 1.4 1.5 1.6 2.4 2.5 2.6
1
2
2
2
2
3

This uses "banker's rounding"
<https://en.wikipedia.org/wiki/Rounding#Round_half_to_even>
which is not exactly what your original awk command does, but maybe it's
what you should have been doing all along.  Who knows?  You didn't give
us a lot of background detail to work with.

Anyway, if "I want to round a stream of floats to ints" is your actual
goal, and if you're sure the input is going to be floats and nothing else,
you could do a lot worse than:

send_multiple_floats | xargs printf '%.0f\n'



reply via email to

[Prev in Thread] Current Thread [Next in Thread]