この質問 の背景部分のフォローアップ。
bash
では、zsh
${(P)FOO}
で二重置換に${!FOO}
を使用できます。どちらも、旧式(hack-y)のeval \$$FOO
が機能します。
したがって、私にとって最もスマートで最も論理的なものは、double/triple/n置換の${${FOO}}, ${${${FOO}}}…
です。これが期待どおりに機能しないのはなぜですか?
2番目:eval
ステートメントで\
は何をしますか?それは脱出だと思い、eval \$$$FOO
のようなものを不可能にします。すべてのシェルで機能するトリプル/ n置換をどのように行うのですか?
\
は、$$
(現在のプロセスID)の展開を防ぐために使用する必要があります。三重置換の場合、二重評価が必要になるため、各評価での不要な展開を回避するためにエスケープを増やす必要があります。
#! /bin/bash
l0=value
l1=l0
l2=l1
l3=l2
l4=l3
echo $l0
eval echo \$$l1
eval eval echo \\$\$$l2
eval eval eval echo \\\\$\\$\$$l3
eval eval eval eval echo \\\\\\\\$\\\\$\\$\$$l4
#!/bin/bash
hello=world
echo=hello
echo $echo ${!echo}
FOO
の値が有効な変数名であるとすると(BAR
など)、eval \$$FOO
はBAR
の値を個別の単語に分割し、各単語をワイルドカードとして扱いますパターンを作成し、結果の最初のワードをコマンドとして実行し、他のワードを引数として渡します。ドルの前のバックスラッシュは文字どおりに扱われるため、eval
ビルトインに渡される引数は4文字の文字列$BAR
です。
${${FOO}}
は構文エラーです。いずれの一般的なシェルにもこのような機能がないため(この構文ではありません)、「二重置換」は行われません。 zshでは、${${FOO}}
isは有効であり、二重置換ですが、これは希望する動作とは異なります:FOO
の値に対して2つの連続した変換を実行します。どちらも恒等変換であるため、${FOO}
を書くのはお洒落な方法です。
変数の値を変数として扱う場合は、適切に引用符を付けるように注意してください。結果を変数に設定すると、はるかに簡単になります。
eval "value=\${$FOO}"
{ba,z}sh
解決これは両方で機能する関数です{ba,z}sh
。また、POSIXにも準拠していると思います。
引用に狂わずに、次のように間接参照の多くのレベルで使用できます。
$ a=b
$ b=c
$ c=d
$ echo $(var_expand $(var_expand $a)
d
または、より多くの(!?!)レベルの間接参照がある場合は、ループを使用できます。
指定すると警告します:
# Expand the variable named by $1 into its value. Works in both {ba,z}sh
# eg: a=HOME $(var_expand $a) == /home/me
var_expand() {
if [ -z "${1-}" ] || [ $# -ne 1 ]; then
printf 'var_expand: expected one argument\n' >&2;
return 1;
fi
eval printf '%s' "\"\${$1?}\""
}
なぜあなたはそれをする必要があるのですか?
あなたはいつも次のようないくつかのステップでそれを行うことができます:
eval "l1=\${$var}"
eval "l2=\${$l1}"
...
または、次のような関数を使用します。
deref() {
if [ "$1" -le 0 ]; then
eval "$3=\$2"
else
eval "deref $(($1 - 1)) \"\${$2}\" \"\$3\""
fi
}
次に:
$ a=b b=c c=d d=e e=blah
$ deref 3 a res; echo "$res"
d
$ deref 5 a res; echo "$res"
blah
FWIW、zsh
内:
$ echo ${(P)${(P)${(P)${(P)a}}}}
blah
引用に狂わずに、この関数を間接参照の多くのレベルに使用できます。
$ a=b
$ b=c
$ c=d
$ echo $(var_expand $(var_expand $a)
d
または、より多くの(!?!)レベルの間接参照がある場合は、ループを使用できます。
指定すると警告します:
# Expand the variable named by $1 into its value. Works in both {ba,z}sh
# eg: a=HOME $(var_expand $a) == /home/me
function var_expand {
if [ "$#" -ne 1 ] || [ -z "${1-}" ]; then
printf 'var_expand: expected one argument\n' >&2;
return 1;
fi
eval printf '%s' "\"\${$1?}\""
}
zsh
で${(P)foo}
の代わりに${$foo}
が機能しないのと同様に、${${$foo}}
も機能しません。間接参照の各レベルを指定するだけです。
$ foo=bar
$ bar=baz
$ baz=3
$ echo $foo
bar
$ echo ${(P)foo}
baz
$ echo ${(P)${(P)foo}}
3
もちろん、bash
はネストされた置換を許可しないため、bash
では${!${!foo}}
は機能しません。