如何
infinite_number_loop
在bash中运行a? ,如下所示: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中运行a?
最简单的方法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
它还受时区的影响。
算术笔记
shell 整数的最大值是 i 小于 2,提高到 63:
$ echo $(( (2**63) - 1 ))
9223372036854775807
我们可以通过以下方式获得十进制和十六进制表示:
$ printf '%d %x\n' "$(( (2**63) - 1 ))"{,}
9223372036854775807 7fffffffffffffff
这是 64 位有符号整数可表示的最大数字(当然,如果您的系统是 64 位)。下一个数字(只需加一)将环绕(溢出)为负数:
$ echo $(( (2**63) ))
-9223372036854775808
$ printf '%d %x\n' "$(( (2**63) ))"{,}
-9223372036854775808 8000000000000000
这恰好是有符号 64 位整数的最大负数。
但获得相同结果的更快方法是使用左移,其作用与将数字乘以 2 相同:
$ printf '%d %x\n' "$(( (1<<63) - 1 ))"{,}
9223372036854775807 7fffffffffffffff