Bash script: do something one time inside a loop then stop, but continue looping

push(){
    shuttle push note Chrome \
        "Aurora: $1" \
        "Battery is at $percent percent"
}

full=0
while    percent=$(acpi | awk '{ print $4}' | sed 's/[,%]//g')
do       case $percent:$full in 
         (100:1) ;; (100:0)
              full=1
              push 'Battery charged';;
         (?:*|1?:*|20:*)
              full=0
              push 'Plug in Now';;
         (*1) full=0
         esac
done

The shell's case statement allows you to execute an arbitrary code block based on whether or not a shell pattern can be matched against the value of a shell expansion. In this way you can very simply handle multiple possible outcomes of the very same test.

Above I concatenate the values of $percent and $full on a : colon delimiter, which is a technique I originally picked up from Stephane Chazelas even if I have become fairly good with it on my own right since. Because the code executed needs to depend on both values, testing both simultaneously is the most simple solution.

case pattern blocks are evaluated in order from first to last. As soon as a pattern is matched the associated code is executed and case returns. The first matched pattern is also the last pattern evaluated.

And so if the values of $percent and $full are respectively:

  1. 100 and 1

    • The first pattern is matched, which is associated with an empty code block. No action is taken, and case returns to while.
  2. 100 and 0

    • The second pattern is matched, which sets $full to 1 and calls the shell function push w/ the argument Battery Charged
    • push() is only defined here to organize the code according to purpose.
  3. <=20 and anything at all

    • The third pattern is matched, $full is set to 0, and push is called with the arg Plug in Now.
  4. anything at all and 1

    • The last pattern is matched and $full is set to 0.
  5. Anything else

    • No pattern is matched and no action is taken.

Taken altogether, this means that $full is only set when necessary, push() is only called when $full is 0 and $percent is 100 or $full is anything and $percent is <=20.

All of that would be something of an improvement on what you have already. But the real problem there is:

percent=$(acpi | awk '{ print $4}' | sed 's/[,%]//g')

Forking multiple times like that in a while true is incredibly wasteful. You've gotta come up with another way to do that.


How about:

if [ "$percent" -eq 100 ] && [ "$full_flag" -eq 0 ];
then
    shuttle push note Chrome "Aurora: Battery charged" "Battery is at $percent percent"
    full_flag=1
fi
if [ "$percent" -lt 100 ];
then
    full_flag=0
fi