web-dev-qa-db-ja.com

Bashスクリプト:ループ内で1回実行して停止し、ループを継続する

私はラップトップのバッテリーをチェックしてAndroid通知を送信するBashスクリプトを持っています(シャトル経由で、Pushbullet APIのcliインターフェースとして機能する私が書いたBashスクリプトです)。バッテリーが100%に達すると、ラップトップのプラグを抜くように繰り返し通知します。代わりに、100%の充電を一度通知するだけで、それだけです(つまり、チェックを続けますが通知しません)。ただし、バッテリーが少なくなったときに繰り返し通知するようにしたいので、ラップトップを接続するのを忘れないでください(これは現在、ラップトップに接続しています)。

ループ内でcontinueコマンドとbreakコマンドを使用してこれが可能であると考えていましたが、それが私が望んでいるものかどうかはわかりません(あまり詳しくありません)。

とにかく、Bashを使用してこれを最適に実装する方法に関する提案をいただければ幸いです。これは非常に単純だと思いますが、何らかの理由で取得できません。

これが私のスクリプトです:

#! /bin/bash

while true;
do

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

 if [ "$percent" -le "20" ];
then
    shuttle Push note Chrome "Aurora: Plug in now" "Battery is at $percent percent"
fi  
if [ "$percent" -eq "100" ];
then
    shuttle Push note Chrome "Aurora: Battery charged" "Battery is at $percent percent"
fi

sleep 7m
done
6
Andy Forceno

どうですか:

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
3
user1794469
_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
_

シェルのcaseステートメントを使用すると、シェルパターンをシェル展開の値と照合できるかどうかに基づいて、任意のコードブロックを実行できます。このようにして、まったく同じテストの複数の可能な結果を​​非常に簡単に処理できます。

上記では、_$percent_と_$full_の値を_:_コロン区切り文字で連結しています。これは、自分でかなり上手になったとしても、もともとStephane Chazelasから採用した手法です。以来。実行されるコードは両方の値に依存する必要があるため、両方を同時にテストすることが最も簡単なソリューションです。

caseパターンブロックは、最初から最後の順に評価されます。パターンが一致するとすぐに、関連するコードが実行され、caseが返されます。最初に一致したパターンは、最後に評価されたパターンでもあります。

そして、もし_$percent_と_$full_の値がそれぞれ:

  1. 100と1

    • 空のコードブロックに関連付けられている最初のパターンが一致します。アクションは実行されず、casewhileに戻ります。
  2. 100と0

    • 2番目のパターンが一致し、これは_$full_を1に設定し、引数_Battery Charged_を使用してシェル関数Pushを呼び出します
    • Push()は、目的に応じてコードを編成するためにここでのみ定義されています。
  3. <= 20および何でも

    • 3番目のパターンが一致し、_$full_が0に設定され、Pushが引数_Plug in Now_で呼び出されます。
  4. まったく何でも1

    • 最後のパターンが一致し、_$full_が0に設定されます。
  5. 他に何か

    • 一致するパターンはなく、アクションも実行されません。

つまり、_$full_が必要な場合にのみ設定され、_$full_が0で_$percent_が100または_$full_である場合にのみPush()が呼び出されます。何でも、_$percent_は<= 20です。

これらはすべて、すでに持っているものを改善するものです。しかし、実際の問題は次のとおりです。

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

そのような_while true_での複数回のフォークは信じられないほど無駄です。あなたはそれを行う別の方法を考え出さなければならない。

6
mikeserv

continueは、ループの次の反復にジャンプします。 breakはループを完全に終了します。将来の反復に何らかの影響を与えたい場合は、どちらも役に立ちません。そのためには、何かが異なる動作をする必要があるという情報を覚えておく必要があります。一部の情報を記憶する方法は、それを変数に格納することです。

必要なことを行う1つの方法は、percentの以前の値を変数previousに格納することです。 percentpreviousの両方が100の場合、メッセージを出力しません。

バッテリーを監視するためにこれをバックグラウンドで実行していることを考えると、電力を使いすぎないように努力する必要があります。プロセスの数を最小限に抑えます。

trueでループする代わりに、sleepコマンドでループします。このように、ほとんどの場合、実行中のsleepコマンドを強制終了することにより、スクリプトを強制終了できます。

コードをインデントします。ブロック内のすべて(dothenなど)は、一貫した番号によって、左側に空白を追加する必要があります。

previous=
while sleep 7m
do
  percent=$(acpi | awk '{ gsub(/[^ 0-9]/, ""); print $4 }')
  if [ "$percent" -le "20" ]; then
    shuttle Push note Chrome "Aurora: Plug in now" "Battery is at $percent percent"
  Elif [ "$percent" -eq "100" ] && [ "$previous" -ne "100" ]; then
    shuttle Push note Chrome "Aurora: Battery charged" "Battery is at $percent percent"
  fi
done