infinite_number_loop
Bashで実行する方法は? 、次のように:for i in infinite_number_statement do date +%Y-%m-%d -d "$i day ago" 2>&1 > /dev/null if [ $? -ne 0 ] then echo $i fi done
ローカルコンピュータの端末でこのループを実行すると問題がありますか? (ループを使ってさまざまな範囲の乱数を試しましたが、
for
一度中断されました。)
答え1
infinite_number_loop
Bashで実行する方法は?
最も簡単な方法while :; do ... done
::
let i=0
while :; do
let i++
date +%Y-%m-%d -d "$i day ago" >/dev/null 2>&1 || { echo $i && exit 1; }
done
ローカルコンピュータの端末でこのループを実行すると問題がありますか?
これが余りにも多くの時間を無駄にしていることを実現するまで。
答え2
無限ループの代わりに日付の実際の制限を見つけるより良い方法は次のとおりです。
#!/bin/bash
j=$((1<<61))
i=0
while ((j>0)); do
if date +'%Y-%m-%d' -d "$((i+j)) days ago" >/dev/null 2>&1; then
((i+=j)) ; # printf 'running i %d 0x%x\n' "$i"{,}
else
((j>>=1)); # printf 'new j %d 0x%x\n' "$j"{,}
fi
((k++))
# ((k%10)) || printf 'still running %d 0x%x %d %d' "$i"{,} "$j" "$k"
done
printf "final value of limit %d 0x%x in %d loops\n" "$i"{,} "$k"
これは検索日を次に制限します。
final value of limit 2147483649 0x80000001 in 64 loops
コメント文字の削除#これがどのように行われるかを見てください。
32ビット数ではありません。
これは32ビット数に近いようです。
$ printf '%d\n%d\n' "$(( (1<<31) + 1 ))" "0x80000001"
2147483649
2147483649
実際には、2**31+その月の日数です。
12月の最後の日を使用したい場合(上記のスクリプトの5行変更):
date +'%Y-%m-%d' -d "2017-12-31 $((i+j)) days ago"
私達は次を得ました:
final value of limit 2147483679 0x8000001f in 68 loops
上記は31です2**31
。
$ printf '%d\n%d\n' "$(( (2**31) + 31 ))" "0x8000001f"
2147483679
2147483679
時間帯の影響も受けます。
算術ノート
シェル整数の最大値は、iが2より小さく、63に増加します。
$ echo $(( (2**63) - 1 ))
9223372036854775807
次のように10進数と16進数の表現が得られます。
$ printf '%d %x\n' "$(( (2**63) - 1 ))"{,}
9223372036854775807 7fffffffffffffff
これは、64ビット符号付き整数で表示できる最大の数値です(もちろん、システムが64ビットの場合)。次の数字(1つだけ追加した場合)は負の数でラップ(オーバーフロー)されます。
$ echo $(( (2**63) ))
-9223372036854775808
$ printf '%d %x\n' "$(( (2**63) ))"{,}
-9223372036854775808 8000000000000000
これは、符号付き64ビット整数のうち最大の負数です。
しかし、同じ結果を得るより速い方法は、左シフトを使用することです。これは、数に2を掛けるのと同じことを行います。
$ printf '%d %x\n' "$(( (1<<63) - 1 ))"{,}
9223372036854775807 7fffffffffffffff