算術拡張と中括弧拡張を組み合わせることはできますか?
$ for i in {$((1 + 1))..5}; do echo $i; done;
{2..5}
$ echo "bash laughs at me"
答え1
man bash
説明については、以下を参照してください。
拡張順序は、中かっこ拡張、チルダ拡張、パラメータ、変数と算術拡張、コマンド置換(左から右へ)、トークン化、パス名拡張です。
中括弧拡張は算術拡張の前に発生するため、目的の方法で組み合わせることはできません。
代わりに使用してくださいseq
:
for i in $(seq $((a+4)) 12) ; do echo $i ; done
答え2
eval 'for i in {'"$((1 + 1))"'..5}; do echo $i; done'
出力
2
3
4
5
一般的に言えば、実際に実行する前にシェルに何かをするには、シェルに再確認する必要があります。これがeval
関数です。コマンドを2回評価します。シェルは、シェル拡張の評価を解析します。これは通常発生しません。
考えてみてください:
v='"quotes in here"'; printf %s\\n $v
"quotes
in
here"
入力を処理するシェルのパーサーが引用符を解釈するのを見ることができます。今後拡張され、その中の引用符は$v
意味がありません。これを理解できるパーサーはありません。しかし、これを行うと:
v='"quotes in here"'; eval "printf '%s\\n' $v"
quotes in here
出力が突然変わりました。違いはパーサーです。入力のどのビットが命令であるか、その理由は何であるかを決定します。これは;${}()||''""&& while for if
これらすべてに関連する部分です。これは{}
、次の中括弧があなたが要求したものと同じであることを意味するものではありません。他の答えからわかるように、これらは明らかにbash
拡張として別々に扱われます。それにもかかわらず、一般的にこの問題に対処するには、ある種の2番目の評価が必要です。
これがeval
危険を生み出すことです。拡張を参照すると、"${expansion}"
それをパーサーとしてマークします。限界それ。シェルは、何が起こっても、これがコマンドの1つの項目にすぎないことを知っています(引数かもしれません)。それ以外の場合でも、シェルは単純なコマンドの境界を$expansion
越えるコマンドを許可しません。その単純なコマンドはすでに;
分離。しかし - 同様にeval
- シェルが戻って拡張を見直すと、実行できるコマンドを見つけることができます。いくつでも-eval
仕組みはこんな感じです。
したがって、シェルトークンを使用するときに誤ってeval
2回評価しないように非常に注意する必要があります。または- それ以外の場合、シェルが十分に早く完了できない最初のループの部分のみを評価します。ベストプラクティスは、以下のように異なる時点でコマンドの一部のみを個別に評価することです。
これはeval
再び最初の文字列です。
eval \ #inital command
'for i in {'\ #hard-quoted - not expanded or executed
"$((1 + 1))"\ #double-quoted - expanded and delimited
'..5}; do echo $i; done' #hard-quoted - not expanded or executed
上記のコメントは、最初のラウンドで行われた措置について説明しますeval
。まだもう一歩残っています。この場合には、したいので、最初に拡張し、1+1
他のものを拡張しません。私たちが避けることができる最悪の状況は2
。これはシェルパーサートークンではなく、二重引用符内でも安全に評価できます。実際にeval
実際に有効な用途がある場合は、算術にあります。他のすべてのアイテムはハードクォートされています。何もしないで文字列を連結し、他のハードクォート文字列と同様に引用符を削除します。
ただし、2回目の返却時に(最初のステップで引用符を削除した後)、シェルは次を探します。
for i in {2..5}; do echo $i; done
それをしました。
この問題を解決する別の方法があります。
bash -c "for i in {$((1+1))..5}; do echo \$i; done"
^有効です。
そして:
. /dev/fd/0 <<CMD
for i in {$((1+1))..5}; do echo \$i; done
CMD
^有効です。それぞれの場合、ロジックは同じです。つまり、中括弧拡張が評価される前に変数が評価されますbash
。
しかし、私はスタンド拡張機能をたくさん使ったことがありません。私は通常以下が好きです:
until [ $((i=$i+1)) -gt 5 ]
do printf %d\\n $i
done