web-dev-qa-db-ja.com

条件付きバッシュ

2つの条件でしばらく待機し、ディレクトリ内に複数のファイルがあるかどうかを確認します。これらの条件のいずれかがtrueの場合、ループを実行する必要があるため、通常のOR論理ステートメントです。

期待される動作とは、ディレクトリ内またはディレクトリ内の時間の順番が複数の場合、ファイルループが終了することを意味します。現在、複数のファイルをまだ待っています。誰かが私のwhileループが期待どおりに機能しない理由を私に説明できますか?

i=1; while [ $i -le 10 ] || [ $( ls /opt/hosts/ | wc -l ) -lt 2 ]; do sleep 3 $(( i++ )); done

残念ながら、1行のコマンドが必要です。なぜ私のコマンドは無限ループなのかと疑問に思っていますORが機能するはずのように機能しない:(私は最初の条件を3に変更しました)

+ echo Host_name 
+ i=1 
+ '[' 1 -le 2 ']' 
+ sleep 1 1 
+ '[' 2 -le 2 ']' 
+ sleep 1 2 
+ '[' 3 -le 2 ']' 
++ ls /opt/hosts/ 
++ wc -l 
+ '[' 1 -lt 2 ']' 
+ sleep 1 3 
+ '[' 4 -le 2 ']' 
++ ls /opt/hosts/ 
++ wc -l 
+ '[' 1 -lt 2 ']' 
+ sleep 1 4 
+ '[' 5 -le 2 ']' 
++ ls /opt/hosts/ 
++ wc -l 
+ '[' 1 -lt 2 ']' 
+ sleep 1 5 ```
3
adamo89
  • sleepに2つの引数_3_と$(( i++ ))を指定しています。 GNUツールの場合、これはループが各反復で増加する秒数をスリープさせることを意味します。GNU以外のシステムでは、sleepからエラーが発生します。

  • テストの記述方法により、ループは常に少なくとも 10回実行されます。テストでは、「_$i_が10以下である間繰り返す」と表示されます。 _$i_が10に達すると、テストの他の部分が有効になります。ここでは、ORではなく論理ANDテストが必要です。

  • ディレクトリ内のファイルをカウントするために_ls | wc -l_を使用しないでください。これは、(病理的ではあるが)いくつかのケースで誤った結果をもたらします。

代わりに:

_i=1
while [ "$i" -ne 10 ]; do
    set -- /opt/hosts/*
    if [ "$#" -ge 2 ]; then
        break
    fi

    sleep 3         # or, to sleep longer and longer: sleep "$(( 3 + i ))"
    i=$(( i + 1 ))
done
_

これにより、各反復でiが適切にインクリメントされます。また、より安全な方法を使用して、_/opt/hosts_ディレクトリ内の名前の数をカウントします(ディレクトリ内の_*_グロブを展開して数をカウントすることにより)それが展開される名前の)。名前の数が2つ以上になるか、_$i_が値10に達すると、ループは終了します。

ループの後、_$i_が10の場合、ファイルの具体化に失敗しました。

位置パラメーターを保持する必要がある場合(これらはsetコマンドによって上書きされます)、グロブをnames=( /opt/hosts/* )で配列に展開し、次に_"${#names[@]}"_を使用して_"$#"_を使用する代わりに、その配列の長さ。

次のように書くこともできます

_i=1
while [ "$i" -ne 10 ] && ( set -- /opt/hosts/*; [ "$#" -lt 2 ] )
do
    sleep 3
    i=$(( i + 1 ))
done
_

setがサブシェルで実行されているため、これは既存の位置パラメーターを破壊しません。また、あなたが自分で試みたもののタイプに近いでしょう。


「ワンライナー」として:

_i=1; while [ "$i" -ne 10 ]; do set -- /opt/hosts/*; [ "$#" -ge 2 ] && break; sleep 3; i=$(( i + 1 )); done
_

または、forに「算術bashループ」を指定します。

_for (( i=1; i<=10; i++ )); do set -- /opt/hosts/*; [ "$#" -ge 2 ] && break; sleep 3; done
_

または、ディバイダーの上にある最後のコードで:

_i=1; while [ "$i" -ne 10 ] && ( set -- /opt/hosts/*; [ "$#" -lt 2 ] ); do sleep 3; i=$(( i + 1 )); done
_
9
Kusalananda