最初の方法はなぜこのように動作しますか?

最初の方法はなぜこのように動作しますか?

シェルスクリプトをデバッグしようとしています。

content_type='-H "Content-Type: application/json"'
curl $content_type -d "{"test": "test"}" example.com

私はこれが私の期待に応えないことを学びました。このスクリプトでcurl渡されるパラメータは$content_type次のとおりです。

  1. -H
  2. "Content-Type:
  3. application/json"

(私が期待していたもの)代わりに:

  1. -H
  2. Content-Type: application/json

私は次のスクリプトを書くことができることを知っています。

content_type="Content-Type: application/json"
curl -H "$content_type" -d "{"test": "test"}" example.com

このように動作しますが、単一の変数にパラメータを含む複数のスペースを格納できるかどうかを知りたいです。

2番目の(動作)バージョンでは、コンテンツタイプを除外するには-H2つを削除する必要があります$content_type

ただし、オプションとその値を単一のエンティティに入れてコンテンツタイプを除外/含めると、単一のエンティティが削除/追加されるようにすることができるかどうかを知りたいです。

POSIXには配列がないため、配列を使用することは移植性がありません。

最初の方法はなぜこのように動作しますか?

なぜなら噴射。私たちが次のように定義するときcontent_type

content_type='-H "Content-Type: application/json"'

次の値を持ちます。

-H "Content-Type: application/json"

引用符なしで変数を引用すると(代わりに$content_type"$content_type"、拡張値がトークン化の対象になります。 ~から単語分割ページバッシュマニュアル:

シェルは、単語分割のために二重引用符内に表示されない引数拡張、コマンド置換、および算術拡張の結果をスキャンします。

同じページから:

シェルは各文字を$IFS区切り文字として扱い、これらの文字をフィールド終端として使用して、他の拡張結果を単語に分割します。 IFSが設定されていない場合、またはその値が次の場合 <space><tab><newline>...

したがって、-H "Content-Type: application/json"これを区切り文字として使用します。<space><tab><newline>これは私たちに以下を提供します。

-H
"Content-Type:
application/json"

答え1

持つ一つ標準シェルの標準配列と同様の構造です。位置パラメータです。したがって、考えられる解決策はおよびset -- …を使用することです"$@"。あなたの場合は、次のことを行います。

set -- -H "Content-Type: application/json"
curl "$@" -d "{"test": "test"}" example.com

これは、使用可能なアレイを1つだけに制限することに注意してください。たとえば、1つはカールに使用され、もう1つは別のプログラムには使用できません。もちろん、スクリプトのパラメータは破損します。

答え2

関連部分のコピーザイルズの答え:

コマンドを変数に保存するには?

「コマンド」とは、3つのことを意味します。コマンド名(フルパスのある/なしの実行可能ファイルの名前、組み込みまたはエイリアス付きの関数名)、パラメータ付きのコマンド名、またはシェルコードの断片。したがって、変数に保存する方法はいくつかあります。

コマンド名がある場合は、保存して通常どおり二重引用符を含む変数を使用してください。

command_path="$1"
"$command_path" --option --message="hello world"

引数を取るコマンドがある場合、問題は上記のファイル名のリストと同じです。つまり、文字列ではなく文字列のリストです。途中にスペースがある文字列にはパラメータを入力できません。これは、パラメータの一部であるスペースとパラメータを区切るスペースの違いがわからないためです。シェルに配列がある場合はそれを使用できます。

cmd=(/path/to/executable --option --message="hello world" --)
cmd=("${cmd[@]}" "$file1" "$file2")
"${cmd[@]}"

使用しているシェルに配列がない場合はどうなりますか?位置パラメータを変更しても問題ない場合は引き続き使用できます。

set -- /path/to/executable --option --message="hello world" --
set -- "$@" "$file1" "$file2"
"$@"

複雑なシェルコマンド(リダイレクト、パイプなど)を保存する必要がある場合はどうすればよいですか?または、場所パラメータを変更したくない場合は?その後、そのコマンドを含む文字列を作成し、組み込みevalコマンドを使用できます。

code='/path/to/executable --option --message="hello world" -- /path/to/file1 | grep "interesting stuff"'
eval "$code"

誰でも記事全体を読んでください。ザイルズの答えしかし。これには有用な情報がたくさん含まれています。

関連情報