Docker(たとえば)の簡単なインストール方法の1つは次のとおりです。
curl -sSL https://get.docker.com/ | sh
ただし、次のようなものも見ました(Dockerの例を使用)。
sh -c "$(curl -sSL https://get.docker.com/)"
機能的には同じように見えますが、どちらを使用するのですか?それとも単に好み/美的ですか?
(注意してください、出所が不明なスクリプトを実行するときは十分注意してください。)
実用的な違いがあります。
_curl -sSL https://get.docker.com/ | sh
_は、curl
とsh
を同時に開始し、curl
の出力とsh
の入力を接続します。 curl
はsh
がスクリプトを実行できるのと同じ速さでダウンロードを実行します。サーバーは タイミングの不規則性を検出 でき、リソースをファイルまたはバッファーに単にダウンロードするとき、またはブラウザーで表示するときに、見えない悪意のあるコードを挿入できます。
sh -c "$(curl -sSL https://get.docker.com/)"
では、curl
が実行される前にsh
が厳密に実行されます。リソースのコンテンツ全体がダウンロードされ、sh
が開始される前にシェルに渡されます。シェルは、sh
が終了したときにのみcurl
を開始し、リソースのテキストをそれに渡します。サーバーはsh
呼び出しを検出できません。接続が終了した後にのみ開始されます。最初にスクリプトをファイルにダウンロードするのと同じです。
(これはdockerの場合には関係ないかもしれませんが、一般的に問題である可能性があり、2つのコマンドの実際的な違いを強調しています。)
私はそれらが実質的に同一であると信じています。ただし、まれに異なる場合があります。
$(cmd)
はcmd
の結果に置き換えられます。その結果コマンドの長さがgetconf ARG_MAX
によって返される引数の最大長値を超えると、結果が切り捨てられ、予期しない結果になる可能性があります。
Pipeオプションにはこの制限はありません。 curl
コマンドからの出力の各行は、パイプから到達するときにbash
によって実行されます。
ただし、ARG_MAXは通常256,000文字の範囲です。 Dockerのインストールでは、どちらの方法を使用しても自信があります。 :-)
_curl -sSL https://get.docker.com/ | sh
_内:
両方のコマンドcurl
とsh
は、それぞれのサブシェルで同時に開始されます
curl
からのSTDOUTは、STDINとしてsh
に渡されます(これは、パイプ__|
_が行うことです)
sh -c "$(curl -sSL https://get.docker.com/)"
では:
コマンド置換$()
が最初に実行されます。つまり、curl
がサブシェルで最初に実行されます
コマンド置換$()
は、curl
からのSTDOUTに置き換えられます
_sh -c
_(非インタラクティブ、非ログインシェル)はcurl
からSTDOUTを実行します
パイプバージョンでは、curl stdoutをsh stdinにパイプすると、スクリプトの対話性が失われます。そのため、入力を待つときにキーボード入力やスクリプトがクラッシュすることはありません。
ここでの他の答えも重要です。
2つの違い(Web上の他の回答から取得)の1つは、スクリプト全体を一度にダウンロードしない場合、不明なポイントでスクリプトの途中で切断され、コマンドの意味が次のようになることです。実行されました。したがって、最初にファイル全体をダウンロードしてから、それを評価する方が良いようです。