以下から: シェルコマンド置換の予期しない動作
多数の引数を受け入れるコマンドがあります。その中には合法的に空白(およびおそらく他のもの)を含めることができます。
引用符を使用してこれらのパラメータを生成するスクリプトを作成しましたが、出力をコピーして貼り付ける必要があります。
./somecommand
<output on stdout with quoting>
./othercommand some_args <output from above>
私はこれを簡単に単純化しようとしました。
./othercommand $(./somecommand)
上記の予期しない動作が発生しました。問題は、othercommand
一部のパラメーターに引用が必要で、変更できない場合にパラメーターを生成するためにコマンド置換を確実に使用できることです。
ベストアンサー1
私は引用符を使用してこれらのパラメータを生成するスクリプトを作成しました。
シェルの出力が正しく引用されたら、そしてあなたはその結果を信頼しますその後、実行できますeval
。
配列をサポートするシェルがあると仮定すると、シェルを使用して取得した引数を格納するのが最善です。
./gen_args.sh
同様の出力が生成されたら、'foo bar' '*' asdf
実行してeval "args=( $(./gen_args.sh) )"
呼び出しの結果で配列を埋めることができます。 、、、のargs
3つの要素です。foo bar
*
asdf
"${args[@]}"
通常どおり、配列要素を個別に拡張できます。
$ eval "args=( $(./gen_args.sh) )"
$ for var in "${args[@]}"; do printf ":%s:\n" "$var"; done
:foo bar:
:*:
:asdf:
(引用符に注意してください。"${array[@]}"
変更されていない一意の引数ですべての要素に展開されます。引用符がない場合、配列要素はトークン化の影響を受けます。BashGuideの配列ページ.)
しかし、、eval
すべてのシェル置換がうまく実行されるため、出力は$HOME
ホームディレクトリに展開され、コマンド置換は実際に実行されているシェルでコマンドを実行しますeval
。の出力は"$(date >&2)"
空の配列要素を生成し、標準出力に現在の日付を印刷します。これはgen_args.sh
、信頼できないソース(ネットワーク上の他のホスト、他のユーザーが作成したファイル名)からデータを取得した場合に問題になります。出力には任意のコマンドを含めることができます。(get_args.sh
悪意のある場合は、何も出力せずに悪意のある命令を直接実行するだけです。)
シェル引用の代替案は、スクリプト出力で別の文字を区切り文字として使用することです。シェルの引用は評価なしで解析するのが難しいからです。実際のパラメータでは、必要ないものを選択する必要があります。
選択#
してスクリプトを出力してみましょうfoo bar#*#asdf
。今私達は利用できます引用しないコマンド拡張は、コマンドの出力をパラメータに分割します。
$ IFS='#' # split on '#' signs
$ set -f # disable globbing
$ args=( $( ./gen_args3.sh ) ) # assign the values to the array
$ for var in "${args[@]}"; do printf ":%s:\n" "$var"; done
:foo bar:
:*:
:asdf:
IFS
スクリプトの他の場所で単語の区切りを使用する場合(デフォルト値に設定する必要がある場合unset IFS
)、後でこれを設定する必要がset +f
あります。後でワイルドカードを使用する場合は、ワイルドカードも使用できます。
Bashや配列を含む他のシェルを使用しない場合は、位置引数を使用できます。代わりにthenargs=( $(...) )
に置き換えて使用してください。 (ここでも引用符が必要です。そうしないと、位置パラメータは単語の分離の影響を受けます。)set -- $(./gen_args.sh)
"$@"
"${args[@]}"
"$@"