次のスクリプトが期待どおりに機能するのはなぜですか(印刷hello
)
#!/bin/bash
foo=$(bash -c 'echo hello')
echo $foo
そしてこのスクリプトは:
#!/bin/bash
cmd="bash -c 'echo hello'"
foo=$($cmd)
echo $foo
次のエラーが発生します。
hello': -c: line 0: unexpected EOF while looking for matching `''
hello': -c: line 1: syntax error: unexpected end of file
答え1
存在する、
cmd="bash -c 'echo hello'"
$cmd
あなたが実行しているのはbash -c 'echo hello'
コマンドではなく$cmd
単純なコマンドです。
引用解除は、$cmd
Split + glob演算子を呼び出すことを意味します。ここでは、デフォルト値を使用して$IFS
の内容を、、に$cmd
分割します。したがって、次のように入力したように、この4つのパラメータを使用して実行しています。bash
-c
'echo
hello'
bash
bash -c "'echo" "hello'"
コード'echo
に閉じる引用符がありません(パラメータがこのインラインスクリプトhello'
に含まれています)。$0
$cmd
シェルコードの内容を評価したい場合は、
eval "$cmd"
だから:
cmd="bash -c 'echo hello'"
foo=$(eval "$cmd")
echo "$foo"
Split+glob 演算子を別の方法で使用することもできます。
cmd='bash,-c,echo hello'
IFS=, # split on comma
set -f # disable glob
foo=$($cmd)
echo "$foo"
答え2
内部で実行される内容を見てみましょう$($cmd)
。
$ cmd="bash -c 'echo hello'"
$ foo=$(printf '<%s>' $cmd)
$ echo "$foo"
<bash> <-c> <'echo> <hello'>
ご覧のとおり、実行されたコマンドラインは次のとおりです。
$ foo=$( "bash" "-c" "'echo" "hello'")
式の一方には、実行するコマンド"'echo"
で解析される一重引用符があり、bash -c
閉じる一重引用符が欠落していることを(正しく)報告します。`''
1つの解決策は、evalを使用してコマンドの正しい解析を容易にすることです。
$ foo=$(eval "printf '<%s>' $cmd"); echo "$foo"
<bash><-c><echo hello>
これは働きます:
$ foo=$(eval "$cmd"); echo "$foo"
hello
しかし、正しい考えは次のとおりです。「変数はデータを保存し、関数はコードを保存します」。
$ cmd(){ bash -c 'echo hello'; }
$ foo=$(cmd); echo "$foo"
hello