2

I hope this question is not a duplicate because I've been trying various ways to solve this problem but couldn't find any solution.

I have a for loop in which I'd like to store a command output into a variable whose name is changing every round and would like to do echo and do further operations on these variables, something like this:

for i in `seq 1 $netsize`
do
    echo "node$i: "
    export Bal$i=$(node$i getbalance | bc)  #PIPE TO BC SINCE getbalance RETURNS FLOAT TYPE VALUE
    echo $Bal$i | tee -a <text-file path>   #THIS GIVES ME ONLY 1,2,... NOT THE getbalance VALUE!!!
...

Further I'd like to take node1's getbalance and divide it by sum of total balance of all nodes. So I did this but I get a syntax error:

...
echo "$Bal1/($Bal1+$Bal2+$Bal3+$Bal4+$Bal5+$Bal6+$Bal7+$Bal8+$Bal9)" | bc >> <text-file path> #HERE; I DO THIS FOR A netsize OF 9 NODES ...

How could I solve this second problem for an arbitrary netsize?

  • You are more likely to get a good answer if you give some context to your question: what is getbalance? what does node$i getbalance return, exactly? – steeldriver Jul 7 '15 at 13:16
0

You can do this with a bash array. I would save the values into an index array with something like:

declare -a bal
for i in `seq 1 $netsize`
do
    echo "node$i: "
    bal[$i]=$(node$i getbalance | bc) //PIPE TO BC SINCE getbalance RETURNS FLOAT TYPE VALUE

    echo ${bal[$i]} # should be the right value
done

then to put together the command you want to echo to bc we'll play a little game with a subshell and IFS like

denominator=$(IFS=+; echo "${bal[*]:2}")
equation="${bal[1]}/($denominator)"

Setting IFS=+ will make it so the fields in the array are separated by + when we echo them in the next statement. The ${bal[*]:2} accesses all of the elements of the bal array (that's the ${bal[*]} part). In this case though, we know element number 1 is something we want to skip, since we're going to use that in the numerator of the equation, so we add :2 to take from index 2 on. So ${bal[*]:2} gives us all of the elements of the array bal index 2 and up.

You can see more about bash array slicing in these answers or more here

  • Hey thanks. I'm wondering what ${bal[*]:2} means!? Setting the "+" as IFS doesn't do anything, right? – Aliakbar Ahmadi Jul 7 '15 at 20:14
  • @AliakbarAhmadi I just updated my answer to include more explanation, the IFS=+ is actually pretty important here since that's what will put the + between all the array elements so you can use it in your bc command – Eric Renouf Jul 7 '15 at 23:46
0
netsize=$somenum
(   echo  scale=\(2;  set --                           ### set scale; clear args
    while [ "$#" -lt "$netsize" ]                      ### counts up to $netsize
    do    set "$@" "bal$(($#+1))"                      ### saves arg count as bal$#+1
    ###   I do the below because you don't say what 
    ###   node$# getbalance does. Here it's random.
          eval "node$#(){ "':& x=${!%??} y=${!#"$x"}
                               echo "$x.$y/'"$#\"; }"  ### fakes a node$#() function
          printf "%bbal$#=%b" ');"' '";' \(            ### );"bal$#=";(bal$#=
          "node$#" getbalance                          ### $x.$y/$#\n
    done; printf %b+ ");($1/($@))\c"                   ### );(bal1/(bal1+...bal$netsize))
) | paste -sd\\0 | bc                                  ### removes all \newlines but last

So it's not at all easy to tell what you're trying to do, but one thing I noticed it that you seem to think you need to call a separate bc for every arithmetic evaluation. The opposite is by far the more convenient way to go - bc is an interactive program, besides being a fully-fledged scripting language all its own. It will set and store variable values for the duration of its uptime - and so rather than trying to store all of that information in the shell, just pipe out your equations and let bc store their values for you.

For example:

{ echo 'x=1+2'; echo '"x=";x'; }| bc

...which prints...

x=3

Because first bc evaluates x to equal 1+2, then it prints the arbitrary string "x=" to stdout, and last it is asked to print x's value, and so it does.

The script above does virtually the same thing. For each iteration bal is incremented by one. And it prints out to bc:

);"bal$#=";(bal$#=$!/$#\n

...and then when it has incremented $# to match $netsize it prints (given an example $netsize of 10)...

(bal1/(bal1+bal2+bal3+bal4+bal5+bal6+bal7+bal8+bal9+bal10))

None of those values are saved at all in the shell - bc remembers them all, though. And when you enclose a bc expression in ( parens ) it is evaluated and printed.

I use paste -sd\\0 to remove all \newlines because I don't know what "node$#" getbalance should do, but I assume it will follow its output with a \newline, and (bal1=node$# getbalance's output\n) is a syntax error. So I clear them all and separate the expressions with ; semicolons instead (which I am rather hoping is not a char node$# getbalance will print). To get a clear picture of what the whole script prints, you can replace the bc at the tail w/ tr \; \\n.

Here, for example, is what it will print if netsize=10:

scale=(2)
"bal1="
(bal1=261.40/1)
"bal2="
(bal2=261.41/2)
"bal3="
(bal3=261.42/3)
"bal4="
(bal4=261.44/4)
"bal5="
(bal5=261.45/5)
"bal6="
(bal6=261.46/6)
"bal7="
(bal7=261.48/7)
"bal8="
(bal8=261.49/8)
"bal9="
(bal9=261.52/9)
"bal10="
(bal10=261.54/10)
(bal1/(bal1+bal2+bal3+bal4+bal5+bal6+bal7+bal8+bal9+bal10))

All of that is run through bc which prints something like:

bal1=261.40
bal2=130.70
bal3=87.14
bal4=65.36
bal5=52.29
bal6=43.57
bal7=37.35
bal8=32.68
bal9=29.05
bal10=26.15
.34

If you can be clearer about your input and output I can help to more specifically tailor this to your needs - but doing var=$(echo stuff at | bc) once per iteration is wasteful.

  • Thanks for your answer. Actually what I've written as "node$i getbalance" is in reality "bitcoin-cli -datadir=""$DATADIR"/node$i/" getbalance" ..I've created directories for each Bitcoin daemon instance it start from (there are config-files in directories) and would like to do some calculations with the float type output of my command. – Aliakbar Ahmadi Jul 8 '15 at 14:16
  • @AliakbarAhmadi - right, I know it wasn't $# in your code, because your iterator was $i. But in this one it's $#. I usually like to let the shell do the count implicitly - as I do with $# - rather than insist on doing the adding myself. So that's a directory? What are the extra "" for? And can you quote me a typical line of output for a single call to "node$i" getbalance? – mikeserv Jul 8 '15 at 14:24
  • Oh I see, now I'm learning some new stuff, thanks!..Extra "" were only to distinguish code from comment text, nothing important actually..A typical balance output is just something like 123.00000000. – Aliakbar Ahmadi Jul 8 '15 at 17:32
  • @AliakbarAhmadi Oh, cool. That's easier. – mikeserv Jul 8 '15 at 18:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.