我想在bash中以0.02为增量尝试进行for循环
for ((i=4.00;i<5.42;i+=0.02))
do
commands
done
但这没用。
bc
,但是在4.52上停止可能会比较棘手。使用@roaima建议,在步骤2中使用辅助变量,并使用i=$(echo $tmp_var / 100 | bc)
我想在bash中以0.02为增量尝试进行for循环
for ((i=4.00;i<5.42;i+=0.02))
do
commands
done
但这没用。
bc
,但是在4.52上停止可能会比较棘手。使用@roaima建议,在步骤2中使用辅助变量,并使用i=$(echo $tmp_var / 100 | bc)
Answers:
阅读bash
手册页将提供以下信息:
for (( expr1 ; expr2 ; expr3 )) ; do list ; done
首先,
expr1
根据以下在算术评估中所述的规则对算术表达式进行求值。[...]
然后我们得到这个部分
算术评估
该Shell允许在某些情况下对算术表达式进行求值(请参阅
let
anddeclare
内置命令和算术扩展)。以固定宽度的整数进行评估,不检查溢出[...]
因此可以清楚地看到,您不能使用for
具有非整数值的循环。
一种解决方案可能就是将所有循环组件乘以100,然后在以后使用它们的地方允许这样做,如下所示:
for ((k=400;k<542;k+=2))
do
i=$(bc <<<"scale=2; $k / 100" ) # when k=402 you get i=4.02, etc.
...
done
k=400;k<542;k+=2
它还避免了潜在的浮点运算麻烦。
bc
),派生一个进程,创建一个临时文件(用于here-string),bc
在其中执行(这意味着加载可执行文件和共享库,初始化它们),等待它并进行清理。运行bc
一次以执行循环会更有效率。
i=$(bc <<< "scale...")
或i=$(echo "scale..." | bc)
<<<
来自)中更快,bash
并且ksh
。请注意,切换到其他shell bash
比使用其他语法会带来更好的性能提升。
<<<
(zsh的,mksh,ksh93的,佳日)也支持浮点算术(zsh
,ksh93
,yash
))。
避免在Shell中循环。
如果要算术,请使用awk
或bc
:
awk '
BEGIN{
for (i = 4.00; i < 5.42; i+ = 0.02)
print i
}'
要么
bc << EOF
for (i = 4.00; i < 5.42; i += 0.02) i
EOF
请注意awk
(与相对bc
)与您的处理器double
浮点数表示形式(可能是IEEE 754类型)一起使用。结果,由于这些数字是这些十进制数字的二进制近似值,因此您可能会有些意外:
$ gawk 'BEGIN{for (i=0; i<=0.3; i+=0.1) print i}'
0
0.1
0.2
如果添加OFMT="%.17g"
,则可以看到丢失的原因0.3
:
$ gawk 'BEGIN{OFMT="%.17g"; for (i=0; i<=0.5; i+=0.1) print i}'
0
0.10000000000000001
0.20000000000000001
0.30000000000000004
0.40000000000000002
0.5
bc
做任意精度,所以没有这种问题。
请注意,在默认情况下(除非你修改与输出格式OFMT
或使用printf
有明确的格式规范),awk
用途%.6g
,用于显示浮点数,因此会切换到1E6及以上浮点数上述1,000,000,截断高数的小数部分(100000.02将显示为100000)。
如果您确实确实需要使用Shell循环,例如由于您想为该循环的每次迭代运行特定的命令,请使用具有浮点运算支持的Shell(例如)zsh
,yash
或ksh93
使用上述一个命令生成值列表(或者,seq
如果有的话)并循环其输出。
喜欢:
unset -v IFS # configure split+glob for default word splitting
for i in $(seq 4 0.02 5.42); do
something with "$i"
done
要么:
seq 4 0.02 5.42 | while IFS= read i; do
something with "$i"
done
除非您突破处理器浮点数的限制,否则seq
与上述awk
版本相比,可以更优雅地处理浮点近似所引起的错误。
如果您没有seq
(一个GNU命令),则可以使用以下函数来使它更可靠:
seq() { # args: first increment last
bc << EOF
for (i = $1; i <= $3; i += $2) i
EOF
}
这样对诸如此类的东西会更好seq 100000000001 0.000000001 100000000001.000000005
。但是请注意,如果要将数字传递给不支持数字的命令,则具有任意高精度的数字将无济于事。
unset IFS
在第一个示例中需要?
IFS=$'\n'
但并非在所有shell中都有效。或者,IFS='<a-litteral-newline-here>'
但这不是很清晰。或者,我们可以拆分单词(空格,制表符,换行符),就像您使用默认值$ IFS所获得的一样,或者如果您未设置IFS并且也可以在此处使用。
IFS
,因为我们知道seq
的输出没有需要避免分裂的空格。大部分用于确保您意识到该示例取决于IFS
,这对于其他列表生成命令可能很重要。
使用“ seq”-打印数字序列
seq FIRST INCREMENT LAST
for i in $(seq 4.00 0.02 5.42)
do
echo $i
done