私が試した増分として0.02のbashでforループを作成したい
for ((i=4.00;i<5.42;i+=0.02))
do
commands
done
しかし、それはうまくいきませんでした。
bc
、4.52で停止するのは難しい場合があります。@roaima提案を使用して、2のステップと補助VARを有し、使用i=$(echo $tmp_var / 100 | bc)
私が試した増分として0.02のbashでforループを作成したい
for ((i=4.00;i<5.42;i+=0.02))
do
commands
done
しかし、それはうまくいきませんでした。
bc
、4.52で停止するのは難しい場合があります。@roaima提案を使用して、2のステップと補助VARを有し、使用i=$(echo $tmp_var / 100 | bc)
回答:
bash
manページを読むと、次の情報が得られます。
for (( expr1 ; expr2 ; expr3 )) ; do list ; done
まず、算術式
expr1
は、以下の「算術評価」で説明する規則に従って評価されます。[...]
そして、このセクションを取得します
算術評価
シェルを使用すると、特定の状況下で算術式を評価できます(
let
およびdeclare
組み込みコマンドと算術展開を参照)。評価はオーバーフローのチェックなしで固定幅整数で行われます[...]
したがって、for
整数以外の値でループを使用できないことがはっきりとわかります。
1つの解決策は、単純にすべてのループコンポーネントを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
ループを実行するために1回実行すると、はるかに効率的になります。
i=$(bc <<< "scale...")
またはi=$(echo "scale..." | bc)
<<<
から来たのか)bash
およびで高速ksh
です。bash
とにかく、他のシェルに切り替えると、他の構文を使用するよりもパフォーマンスが向上することに注意してください。
<<<
(zshの、mksh、は、ksh93、ヤシュ)も(浮動小数点演算をサポートしzsh
、ksh93
、yash
))。
シェルでのループを避けます。
算術演算を行う場合は、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タイプなど)で機能することに注意してください。結果として、これらの数値はこれらの10進数の2進近似であるため、いくつかの驚きがあるかもしれません。
$ 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
れるため、1,000,000を超える浮動小数点数の場合は1e6以上に切り替え、大きい数(100000.02)の小数部分を切り捨てます100000と表示されます)。
あなたは本当に、たとえばあなたがそのループの反復ごとに特定のコマンドを実行したいので、シェルのループを使用し、いずれかのように小数点演算のサポートを浮動してシェルを使用する必要がない場合zsh
、yash
またはksh93
上記のように1つのコマンドで値のリストをまたは生成します(または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'
、すべてのシェルで動作するわけではありません。または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