以下から: シェルコマンド置換の予期しない動作
多数の引数を受け入れるコマンドがあります。その中には合法的に空白(およびおそらく他のもの)を含めることができます。
引用符を使用してこれらのパラメータを生成するスクリプトを作成しましたが、出力をコピーして貼り付ける必要があります。
./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[@]}"
"$@"
答え2
問題は、somecommand
スクリプトがoptionsを出力すると、そのothercommand
オプションは実際にはテキストにすぎず、シェルの標準構文解析($IFS
現在進行中の操作と適用されるシェルオプションの影響を受けます)。いいえ制御可能)。
somecommand
使用する代わりに出力オプションを使用すると、より簡単で安全で強力になります。呼ぶ othercommand
。スクリプトsomecommand
は次のとおりです。ラッパースクリプトラッパースクリプトは、他のオプションセットで他の同様のツールを呼び出すためのツールを提供する非常に一般的な方法ですothercommand
。内部コマンドは実際にはシェルスクリプトラッパーです)。otherscript
file
/usr/bin
bash
または、配列を使用して、次のように個々のオプションをksh
保持zsh
するラッパースクリプトを簡単に作成できますothercommand
。
options=( "hi there" "nice weather" "here's a star" "*" )
options+=( "bonus bumblebee!" ) # add additional option
それからothercommand
(まだラッパースクリプトにあります)、次を呼び出します。
othercommand "${options[@]}"
拡張により、"${options[@]}"
配列内の各要素が個別に参照され、別々の引数として表示されますoptions
。othercommand
ラッパーユーザーは実際に呼び出していることを忘れてしまいますothercommand
。いいえスクリプトが単にコマンドラインオプションを出力として生成する場合はothercommand
Trueです。
でオプションを保存する/bin/sh
には、次の手順を$@
実行します。
set -- "hi there" "nice weather" "here's a star" "*"
set -- "$@" "bonus bumblebee!" # add additional option
othercommand "$@"
(位置引数などをset
設定するために使用されるコマンドです。これは標準のPOSIXシェルの配列の内容です。最初の文字はオプションがなく、引数のみがあることを示します)$1
$2
$3
$@
--
set
--
本物最初の値が)で始まる場合にのみ必要です-
。
要素が個別に単語に分割されないように(ファイル名のグロービングも含む)、二重引用符で囲む必要があります$@
。${options[@]}
答え3
出力が安定していて良いシェル構文の場合は、somecommand
次のものを使用できますeval
。
$ eval sh test.sh $(echo '"hello " "hi and bye"')
hello
hi and bye
ただし、出力に有効な参照などがあることを確認する必要があります。そうしないと、スクリプトの外でもコマンドが実行される可能性があります。
$ cat test.sh
for var in "$@"
do
echo "|$var|"
done
$ ls
bar baz test.sh
$ eval sh test.sh $(echo '"hello " "hi and bye"; echo rm *')
|hello |
|hi and bye|
rm bar baz test.sh
echo rm bar baz test.sh
これはスクリプトに渡されず、別々;
のコマンドで実行されます。これを強調するために|
サラウンドを追加しました。$var
通常、出力が完全に信頼できない場合、somecommand
出力を使用してコマンド文字列を安定させることはできません。