私は、bashで変数を引用する必要があることを読みました。 $ fooの代わりに "$ foo"。しかし、スクリプトを書いているときに、引用符なしでは機能するが引用符なしでは機能するケースを見つけました。
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とその引用がどのように機能するかを理解するための優れたドキュメントはどこにありますか?このスクリプトを書いているとき、ルールを理解するのではなく、試行錯誤を繰り返し始めたように感じます。
基本的に、変数の展開を二重引用符で囲んで、Wordの分割(およびファイル名の生成)から保護する必要があります。しかし、あなたの例では、
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
wget
で2つのオプションを表示するには--mirror
および--no-Host-directories
独立しているため、Word分割を行う必要があります。
これを行うには、より堅牢な方法があります。 bash
またはbash
のような配列を使用する他のシェルを使用している場合は、 glenn jackman's answer を参照してください。 ギレスの回答 は、標準の/bin/sh
。どちらも基本的に、各オプションを個別の要素として配列に格納します。
関連する質問と適切な回答: シェルスクリプトが空白やその他の特殊文字で詰まるのはなぜですか?
変数展開を二重引用符で囲むのが良い目安です。 そうする。次に、ごく少数の場合に注意してください。これらは、上記のエラーメッセージなどの診断メッセージを通じて表示されます。
変数の展開を引用する必要がない必要がないいくつかのケースもあります。ただし、あまり大きな違いはないため、二重引用符を使い続ける方が簡単です。そのようなケースの1つは
variable=$other_variable
もう一つは
case $variable in
...) ... ;;
esac
配列を使用するコーディングの最も堅牢な方法:
wget_options=(
--mirror
--no-Host-directories
--directory_prefix="$1"
)
wget "${wget_options[@]}" "$2/$3"
文字列変数に文字列のリストを保存しようとしています。合いません。変数にアクセスする方法に関係なく、何かが壊れています。
wget_options='--mirror --no-Host-directories'
は、変数wget_options
にスペースを含む文字列を設定します。この時点では、スペースがオプションの一部であるか、オプション間のセパレータであるかを知る方法はありません。
引用符付きの置換wget "$wget_options"
を使用して変数にアクセスすると、変数の値が文字列として使用されます。これは、wget
に単一のパラメーターとして渡されるため、単一のオプションであることを意味します。これは、複数のオプションを意味することを意図していたため、あなたのケースではうまくいきません。
引用符で囲まれていない置換wget $wget_options
を使用すると、文字列変数の値は、「split + glob」というニックネームの付いた展開プロセスを受けます。
$IFS
変数を変更していない場合)。これにより、文字列の中間リストが作成されます。これは、分割プロセスがスペースをセパレーターに変換するため、例では機能しますが、オプションにスペースとワイルドカード文字を含めることができるため、一般的に機能しません。
Ksh、bash、yash、zshでは、配列変数を使用できます。シェル用語の配列は文字列のリストなので、情報が失われることはありません。配列変数を作成するには、変数に値を割り当てるときに配列要素を括弧で囲みます。配列のすべての要素にアクセスするには、"${VARIABLE[@]}"
を使用します—これは、配列の要素からリストを形成する"$@"
の一般化です。ここでも二重引用符が必要であることに注意してください。そうでない場合、各要素はsplit + globを受けます。
wget_options=(--mirror --no-Host-directories --user-agent="I can haz spaces")
wget "${wget_options[@]}" …
単純なshでは、配列変数はありません。位置引数を失ってもかまわない場合は、それらを使用して文字列の1つのリストを保存できます。
set -- --mirror --no-Host-directories --user-agent="I can haz spaces"
wget "$@" …
詳細については、 なぜシェルスクリプトが空白やその他の特殊文字で詰まるのですか? を参照してください。