私のスクリプトにはいくつかの問題がありますが、bash
理由がわかりません。スクリプトは入力(分単位)を秒に変換してからゼロに達するまでカウントダウンを開始し、この時点でスクリプトが停止するように設計されています。
sleep 1
途中で1つを追加すると、カウントダウンが停止します。なぜこれが起こるのですか?
パスワード:
if [ -z $1 ];
then
read NUMBER
else NUMBER=$1
fi
SECONDS=$(( NUMBER * 60 ))
while [ $SECONDS -gt 0 ]
do
sleep 1
SECONDS=$(( SECONDS - 1))
echo $SECONDS remaining
done
echo end
答え1
SECONDS
スクリプトの縮小操作を妨げるように動作するBashで内部的に使用される変数。別の変数名(たとえば、SECS
または)を使用するとseconds
正しく機能します。たとえば、
#!/usr/bin/env bash
if [ -z $1 ];
then
read NUMBER
else NUMBER=$1
fi
SECS=$(( NUMBER * 60 ))
while [ $SECS -gt 0 ]
do
sleep 1
SECS=$(( SECS - 1 ))
echo $SECS remaining
done
echo end
Bashのマニュアルページでは、変数を使用したときに表示される動作について説明しますSECONDS
。
SECONDS
Each time this parameter is referenced, it expands to the number of sec-
onds since shell invocation. If a value is assigned to SECONDS, the
value returned upon subsequent references is the number of seconds since
the assignment plus the value assigned. The number of seconds at shell
invocation and the current time are always determined by querying the
system clock. If SECONDS is unset, it loses its special properties,
even if it is subsequently reset.
への各参照はSECONDS
最後の割り当てから1秒なので、返される値は2番目に割り当てられた最初の値を加えた値です(ループ前while
)。したがって、実際にループを通過するたびに減少がキャンセルされます。
1
))
(また、以前の使用法と一貫性を維持するために、および間にスペースを追加しました。)
答え2
〜のように@SottoVoceが言った。、$SECONDS
bash、Kornなどの他のシェルの特殊変数です。
bashとksh(zshではない)でwithなどの変数を設定解除すると、unset -v SECONDS
特別な意味はなくなりますが、通常は環境スクリプトを除いて、シェルスクリプトではすべて大文字の変数名を使用したくありません。
スクリプトにはいくつかの他の問題があります。
#!/usr/bin/env bash if [ -z $1 ];
これは言葉ではありません。周りの引用符を忘れてしまった$1
ので、分割+globとnull削除の影響を受けます。したがって、$1
isがnullまたは設定されていない場合は[ -z ]
空でない文字列であることを確認するのと同じであるため、trueを返します。または、この場合、いくつかのエラーが発生します。一部のパラメータが渡されたことを確認するには、パラメータ数()を0と比較します。[ -n -z ]
-z
$1
foo bar
*
$#
if [ "$#" -eq 0 ] # standard
if (( $# == 0 )) # Korn-like shells
then read NUMBER
read
いくつかの文字の削除といくつかのバックスラッシュ処理があります。$IFS
これは数字には適していますが、通常は入力行を読む場合は次のようになります。
IFS= read -r minutes
else NUMBER=$1 fi SECONDS=$(( NUMBER * 60 ))
算術式で削除されていないデータを使用することは、bashや他のKorn様シェルのコマンド注入の脆弱性です。。
あなたは次のようなものが欲しい:
case $minutes in
("" | *[!0123456789]*) echo>&2 invalid number of minutes; exit 1;;
esac
(( seconds = minutes * 60 ))
番号の順序を確認してください。
while [ $SECONDS -gt 0 ]
bashは算術式でゼロで始まる数字を8進数として扱いますが、[
数値比較式ではそうではありません。常に10進数で処理するので、混ぜて使用しないでください。
while (( seconds > 0 ))
do sleep 1 SECONDS=$(( SECONDS - 1)) echo $SECONDS remaining done
次のようにすることもできます。
for (( seconds = minutes * 60; seconds > 0; seconds-- )); do
echo "$seconds seconds remaining"
sleep 1
done
ただし、コマンドの実行にも少し時間がかかることに注意してください。sleep 1
プロセスを分岐してその内で実行するのに少なくとも1マイクロ秒かかり、ループ内でより多くのコマンドを実行する場合は時間がかかるため、1000回実行すると少なくとも1001秒sleep
かかります。
この問題を解決するには、実際に特殊変数を使用できますが、$SECONDS
現在使用している bash 内では使用できません。壊れたそして、浮動小数点にする方法はありません。 zshから:
#! /bin/zsh -
(( $# )) || vared -cp 'Enter the number of minutes: ' 1
[[ $1 = <-> ]] || {
print -u2 Invalid number.
exit 1
}
(( seconds = 60 * $1 ))
typeset -F SECONDS=0
for (( tick = 1; tick <= seconds; tick++ )); do
printf '%g seconds remaining\n' seconds-SECONDS
(( (delay = tick - SECONDS) <= 0 )) || sleep $delay
done