変数の変数を使用してコンパレータを設定し、シェルが各エコーでその変数を拡張するようにします。

変数の変数を使用してコンパレータを設定し、シェルが各エコーでその変数を拡張するようにします。

これは私のスクリプトの一部です。

#!/bin/sh

n=1

echo "How many repetitions to run (0 = no limit)?"
read reps

if [ $reps = 0 ]; then
    while="true"
else
    while="[ $n -lt $((reps+1)) ]"
fi

echo "How much off-time in-between reps (in minutes)?"
read time

pwr_init

while $while; do
    echo "* Sending power pulse $n"
    pwr_normal
    t=$time
    echo "* Waiting for next power on"
    while [ $t -gt 0 ]; do
        echo "    $t min until next power on"; sleep 60
        t=$((t-1))
    done
    n=$((n+1))
done

while私がやっている最初のループを除いて、すべてが私が望む方法で正確に機能しています。変数が呼び出されるたびに$whileシェルが展開され、最初のステートメントで定義されているthen変数を確認したいと思いますif

最初のステートメントから変数を拡張するシェルのみを取得できますが、コンパレータは常にtrueで、最初のステートメントの内容中に変数がすでに拡張されているため、増分したときに変更されないため、if私が取得する動作です。true$n$whileif

${!while}同様のアプローチと一重引用符と二重引用符のさまざまな組み合わせを試しましたが、運がありませんでした。通常$n is a bad number、または同様のエラーが発生しますbad substitution

今私が考えることができる唯一のことは、より多くの関数により多くのステートメントを追加して、この行が毎回チェックされ、ステートメントに反映されるようifにすることです。while私はこれを行うより良い方法があると信じなければなりません。しかし、私はそれを見つけるのに苦労しています(オンラインで検索するための適切なキーワードを見つけるのも難しいです)。

デバッグ時に使用すると、set -xコンパレータ文字列がここでスクリプト化された方法で正しく評価されていることがわかりますが、前述のように増加して$nもそれ自体は更新されません。

このタイプの人生を少し簡単にする代替のヒントを持っている人はいますか?この奇妙な変数拡張の謎の洞察力を提供していただきありがとうございます!

注1:pwr_initおよびpwr_normalは、スクリプト全体の他の場所で定義された関数です。

注2:Shebangに注意してください。 POSIXと互換性がなければなりません。クールなbash固有のスキルは利用できません。

編集:それがうまくいくのと同じようにうまくいくのは思ったより簡単でした。以下に追加した内容を入れました。しかし、このタイプの変数拡張が可能であれば知っておくと良いでしょうので、この質問は開いておきます。

新しいスクリプトのcheck機能は、if次の文に含まれています。

#!/bin/sh

n=1

echo "How many repetitions to run (0 = no limit)?"
read reps

count=$((reps+1))

check() {
    if [ $reps = 0 ]; then
        while="true"
    else
        while="[ $n -lt $count ]"
    fi
}

echo "How much off-time in-between reps (in minutes)?"
read time

pwr_init

while $while; do
    echo "* Sending power pulse $n"
    pwr_normal
    t=$time
    echo "* Waiting for next power on"
    while [ $t -gt 0 ]; do
        echo "    $t min until next power on"; sleep 60
        t=$((t-1))
    done
    n=$((n+1))
    check
done

答え1

非常に簡単な解決策:「無限に」実行するには、制限を非常に大きな数字に設定します。 32ビット整数は最大4294967296までの数字を保持できます。符号ビットを考慮し、下降すると、少なくとも2000000000まで数えることができると仮定することができる。数字を数える間に少なくとも1分間眠るので、これは約4000年間可能です。

だからこうしてください:

if [ "$reps" = 0 ]; then
    reps=2000000000
fi

条件を次のようにハードワイヤーします。

while [ "$n" -le "$reps" ]; do ...

(64ビットシステムを実行している場合はより高く計算できますが、問題はありません。16ビットシステムを実行している場合は、おそらく他の方法で壊れます。)


簡単な解決策:reps0よりn小さいことを確認する関数を作成してくださいreps

keep_going() {
    [ "$reps" = 0 ] && return 0
    [ "$n" -le "$reps" ] 
}

それから条件を出す

while keep_going; do ...

はい、毎回関数を呼び出しています。いいえ、最大1分に1回しか呼び出せないため、問題ありません。 (本当に速度に興味がある場合は、この作業にシェルを使用しないでください。)


あなたが探しているソリューション:使用eval。ペアの割り当てに二重引用符を使用すると、while="[ $n -lt $count ]"すべて$n$countすぐに拡張されます。必要なのは、割り当てに一重引用符を使用することです。while='[ $n -lt $count ]'次に、文字列を実行してevalその中の変数を展開します。

while='[ $n -lt $count ]'
while eval "$while" ; do ...

答え2

他の関数を定義してみることができます。

if [ $reps -eq 0 ]; then
    while_test() { true; }
else
    while_test() { test $1 -lt $((reps+1)); }
fi

それから

while while_test $n; do

evalPOSIX シェルでは、以下を使用して変数の条件を処理できます。

$ cond='[ $n -lt $reps ]'
$ reps=5
$ n=4
$ eval "$cond" && echo 'n < reps' || echo 'n >= reps'
n < reps
$ n=5
$ eval "$cond" && echo 'n < reps' || echo 'n >= reps'
n >= reps

変数が早期に拡張されないように、条件が一重引用符で定義されていることを確認してください。

関連情報