1

Description

Hello,

I'm trying to loop over certain commands and save their outputs into a file, and while looping through those commands, also check the file which we saved their outputs so we can compare them from the file while looping the commands at the same time. In the end, check if the looped commands' outputs match with the previously saved outputs in that file. (Also check if the file doesn't contain the output and add it into the file so we can later use it to compare again)

This is my main script which loops through the said commands which are located inside /usr/local/bin/ so I can run them directly from the shell.

#!/bin/bash
wallets=`find /usr/local/bin/ -iname '*-cli'`

for i in $wallets; do
    current_blocks=`$I getblockcount`
    coin_name=${i:15:-4} # I use `:15:-4` here to cut the path and the last 4 characters. (For example it's `/usr/local/bin/bitcoin-cli` so I change it to `bitcoin` only
    echo $coin_name $current_blocks
    echo $coin_name $current_blocks >> blocks.log
done

And this echo gives us exactly this (assuming there are 2 items in the $wallets;

bitcoin 1457824
litecoin 759345

And this is the while loop I will -presumably- be using to read from the file;

while read line ; do
    set $line
    echo $1 $2
done < blocks.log

Which will also gives us this output when we run it;

bitcoin 1457824
litecoin 759345

So since I have these 2 code bits, now I want to merge them into a single script so I can both use the first code bit to loop through commands and also compare them with the blocks.log file. (Again, also check if the file doesn't contain the output and add it into the file so we can later use it to compare again).

My first (and failed) approach;

for i in $wallets; do

    current_blocks=`$i getblockcount`
    coin_name=${i:15:-4}

    while read line; do
        set $line
        if [ "$1" == "$coin_name" ]; then
            echo "File contains the coin_name, compare the blocks now"
            if (( "$current_blocks" >= "$2" )); then
                echo "Current blocks are greater than the saved blocks"
                echo "Saving the new blocks count now"
                sed -i "s/$1/$1 $current_blocks/" blocks.log
            else
                echo "Current blocks are less than or equals to saved blocks"
            fi
        else
            echo "File does not contain the coin_name, adding it now"
            echo "$coin_name $current_blocks" >> blocks.log
        fi
    done < blocks.log

done

My second (another failed) attempt;

for i in $wallets; do

    current_blocks=`$i getblockcount`
    coin_name=${i:15:-4}

    read line < blocks.log
    set $line
    if [ "$1" == "$coin_name" ]; then
        echo "File contains the coin_name, compare the blocks now"
        if (( "$current_blocks" >= "$2" )); then
            echo "Current blocks are greater than the saved blocks"
            echo "Saving the new blocks count now"
            # sed -i "s/$1/$1 $current_blocks/" blocks.log
        else
            echo "Current blocks are less than or equals to saved blocks"
        fi
    else
        echo "File does not contain the coin_name, adding it now"
        echo "$coin_name $current_blocks" >> blocks.log
    fi

done

What am I doing wrong?

11
  • 1
    read can split the line for you; no need to abuse the positional parameters with set. while read name blocks; do ...; done < blocks.log. Commented Feb 19, 2019 at 15:55
  • I'll keep that in mind, thanks for the tip. Commented Feb 19, 2019 at 16:01
  • 1
    That said, what is it about this code that doesn't work? What does it do, and what do you expect it to do instead? Commented Feb 19, 2019 at 16:04
  • 1
    Having the while both read from and write to blocks.log might be problematic Commented Feb 19, 2019 at 16:05
  • 1
    UsingFind is worth review, btw, just wrt. the wallets=$(find ...) and for i in $wallets; likewise, BashPitfalls #1. Commented Feb 19, 2019 at 18:53

1 Answer 1

1

What am I doing wrong?

In your second attempt you correctly recognized that the problem doesn't call for nested loops, still the read line < blocks.log is wrong because this reads only the first line repeatedly. Here's a corrected version with some remarks following:

line='' # no line read yet
for i in $wallets; do
    current_blocks=`$i getblockcount`
    coin_name=`basename $i -cli`    # I just prefer it more readable.
    if [ ! "$line" ]; then          # we need to read a line from log
        read line
        set -- $line
    fi
    if [ "$1" == "$coin_name" ]; then
        line='' # read new line next time
        echo "File contains the coin_name, compare the blocks now"
        if (( current_blocks > $2 )); then
            echo "Current blocks are greater than the saved blocks"
            echo "Saving the new blocks count now"
            sed -i "/$1/c$1 $current_blocks" blocks.log
        else
            echo "Current blocks are less than or equal to saved blocks"
        fi
    else
        # $line as well as $1 and $2 remain set, will be used in next loop cycle
        echo "File does not contain the coin_name, adding it now"
        if [ ! "$1" ]; then         # $1 is empty at EOF
            # create a new blocks.log to not disturb reading from original:
            cp blocks.log $$; mv $$ blocks.log
            echo "$coin_name $current_blocks" >>blocks.log
        else
            # insert before current line (address /$1/)
            sed -i "/$1/i$coin_name $current_blocks" blocks.log
        fi
    fi
done <blocks.log

Normally, we read one line from the log for each wallet in order, thus we have one for i in $wallets loop with one read line inside. But there's the case when since the last script run a new wallet accrued, which isn't in the file; here we have already read the next wallet line from the log file, so we save this line data for the next loop cycle and do not read a new line then.

Another important point is that we must not modify the blocks.log input file while the loop runs - this would add new to old data, mixing the sheep and the goats. Fortunately, sed -i creates a new blocks.log output file, while the old input file remains anonymously and undisturbed. Only when we echo … >>blocks.log we ourselves must ensure that a new blocks.log is created.

Note that this approach requires the wallets to be sorted; since your -cli files are all in the directory /usr/local/bin/, you can write wallets=/usr/local/bin/*-cli instead of the line with find.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.