引用変数のオプションは失敗しますが、引用されていない変数が機能するのはなぜですか?

引用変数のオプションは失敗しますが、引用されていない変数が機能するのはなぜですか?

$fooの代わりに"$foo"のようにbashで変数を引用しなければならないという内容を読みました。しかし、スクリプトの作成中に引用符なしで動作しますが、引用符を使用すると動作しない状況が見つかりました。

wget_options='--mirror --no-host-directories'
local_root="$1" # ./testdir recieved from command line
remote_root="$2" # ftp://XXX recieved from command line 
relative_path="$3" # /XXX received from command line

これはうまくいきます:

wget $wget_options --directory_prefix="$local_root" "$remote_root$relative_path"

これはそうではありません($wget_optionsの周りの二重引用符に注意してください):

wget "$wget_options" --directory_prefix="$local_root" "$remote_root$relative_path"
  • その理由は何ですか?

  • 最初の行は良いバージョンですか?それとも、この動作を引き起こす隠されたバグがあると疑うべきですか?

  • 一般に、bashとその参照がどのように機能するかについての良いドキュメントはどこにありますか?このスクリプトの作成中に、私はルールを理解するのではなく、試行錯誤に基づいて作業を開始するように感じました。

答え1

最も信頼性の高いエンコード方法は配列を使用することです。

wget_options=(
    --mirror 
    --no-host-directories
    --directory_prefix="$1"
)
wget "${wget_options[@]}" "$2/$3"

答え2

デフォルトでは、トークン化(およびファイル名の生成)から保護するには、二重引用符変数拡張を使用する必要があります。しかし、あなたの例では

wget_options='--mirror --no-host-directories'
wget $wget_options --directory_prefix="$local_root" "$remote_root$relative_path"

噴射まさにあなたが望むもの

"$wget_options"(参照)を使用すると、単一の引数wgetで何をすべきかわからないと--mirror --no-host-directories文句を言います。

wget: unknown option -- mirror --no-host-directories

wget2つのオプションを分離して--mirror表示するには、単語--no-host-directories分割が必要です。

これを行うより強力な方法があります。 do などの配列を使用するbash別のシェルを使用する場合は、次を参照してください。bashグレンジャックマンの答えザイルズの答え標準ハウジングなどの一般ハウジングの代替ソリューションも説明されています/bin/sh。どちらもデフォルトで各オプションを配列の別々の要素として保存します。

良い答えがある関連質問:スペースやその他の特殊文字が原因でシェルスクリプトが停止するのはなぜですか?


二重引用符変数の拡張は良い経験則です。太陽。それから注意してください珍しいこれをしてはいけない状況があります。これは、上記のエラーメッセージなどの診断メッセージを介して表示されます。

そうしない状況もあります必要参照変数の拡張。しかし、とにかく星の違いはないので、二重引用符を使い続ける方が簡単です。その事例の一つは

variable=$other_variable

もう一つは

case $variable in
    ...) ... ;;
esac

答え3

文字列変数に文字列リストを保存しようとしています。そうではありません。変数にどのようにアクセスしても、常に何かが破損します。

wget_options='--mirror --no-host-directories'変数をwget_optionsスペースを含む文字列に設定します。この時点では、スペースがオプションの一部であるか、オプション間の区切り文字であるかはわかりません。

引用符付き置換を使用して変数にアクセスすると、wget "$wget_options"変数の値が文字列として使用されます。つまり、単一の引数として渡されるので、wgetオプションです。いくつかのオプションを意味したいので、これはあなたのケースを台無しにします。

引用符のない置換を使用すると、wget $wget_options文字列変数の値は「split + glob」という別名の拡張プロセスを経ます。

  1. 変数の値を取得し、スペースで区切られた部分に分割します(まだ変数を変更していないと仮定$IFS)。これにより、中間文字列のリストが生成されます。
  2. 中間リストの各要素について、1つ以上のファイルと一致するワイルドカードパターンの場合は、その要素を一致するファイルのリストに置き換えます。

分割プロセスはスペースを区切り文字に変換するため、この例では機能しますが、オプションにスペースとワイルドカードを含めることができるため、通常は機能しません。

ksh、bash、yash、zshでは配列変数を使用できます。シェル用語では、配列は文字列のリストなので、情報は失われません。配列変数を生成するには、変数に値を割り当てるときに配列要素の周りに括弧を追加します。配列内のすべての要素にアクセスするには、配列要素のリストを形成する - を一般化した - を使用します。ここでも二重引用符が必要です。それ以外の場合は、すべての要素が分割+グローブを通過します。"${VARIABLE[@]}""$@"

wget_options=(--mirror --no-host-directories --user-agent="I can haz spaces")
wget "${wget_options[@]}" …

通常のshには配列変数はありません。場所パラメータを失っても問題ない場合は、それを使用して文字列リストを保存できます。

set -- --mirror --no-host-directories --user-agent="I can haz spaces"
wget "$@" …

詳細については、次を参照してください。スペースやその他の特殊文字が原因でシェルスクリプトが停止するのはなぜですか?

関連情報