私は次のどれかを使いました
echo $(stuff)
echo `stuff`
(ここでstuff
はpwd
やdate
、あるいはもっと複雑なものです。).
それから私はこの構文が間違っている、悪い習慣、エレガントではない、過度の、冗長な、過度に複雑な、カーゴカルトプログラミング、noobish、素朴なことなどを言われました。
しかし、というコマンドはでも動作します。
唯一のstuff
がおそらく動作します。
foo $(stuff)
を実行すると、次のようになります。
stuff
が実行されます。foo
の呼び出しで$(stuff)
を置き換えます。foo
が実行され、そのコマンドライン引数は明らかにstuff
が返したものに依存します。この$(…)
メカニズムは、「コマンド置換」と呼ばれます。あなたの場合、メインコマンドはecho
で、基本的にコマンドライン引数を標準出力に出力します。したがって、stuff
がstdoutに出力しようとするものはすべてキャプチャされ、echo
に渡され、echo
によってstdoutに出力されます。
stuff
の出力をstdoutに出力する場合は、唯一のstuff
を実行してください。
`…`
構文は$(…)
と同じ目的を果たします(同じ名前:「コマンド置換」)。ただし、違いはほとんどないため、盲目的に交換することはできません。 このFAQ および この質問 を参照してください。
echo $(stuff)
を避けるべきですか?自分が何をしているのかわかっているなら、echo $(stuff)
を使いたくなる理由があります。同じ理由で、何をしているのかわからない場合はecho $(stuff)
を避けるべきです。
ポイントはstuff
とecho $(stuff)
は完全に同等ではありません。後者は、$IFS
のデフォルト値でstuff
の出力でsplit + glob演算子を呼び出すことを意味します。コマンド置換を二重引用符で囲むと、これが防止されます。コマンド置換を単一引用符で囲むと、コマンド置換ではなくなります。
分割に関してこれを観察するには、次のコマンドを実行します。
echo "a b"
echo $(echo "a b")
echo "$(echo "a b")" # the Shell is smart enough to identify the inner and outer quotes
echo '$(echo "a b")'
そしてグロビングのために:
echo "/*"
echo $(echo "/*")
echo "$(echo "/*")" # the Shell is smart enough to identify the inner and outer quotes
echo '$(echo "/*")'
ご覧のとおり、echo "$(stuff)"
はstuff
と同等です(-ish *)。あなたはそれを使用することができますが、このように物事を複雑にすることのポイントは何ですか?
一方、stuff
の出力を分割+グロビングしたい場合、echo $(stuff)
が有用であることがわかりますmay。しかし、それはあなたの意識的な決定でなければなりません。
評価して(分割、グロブなどを含む)、シェルで実行する必要がある出力を生成するコマンドがあるため、eval "$(stuff)"
が可能です( this answer を参照)。 printedになる前に、追加の分割+グロビングを行うために出力を必要とするコマンドを見たことはありません。意図的にecho $(stuff)
を使用することは非常にまれです。
var=$(stuff); echo "$var"
はどうですか?いい視点ね。このスニペット:
var=$(stuff)
echo "$var"
stuff
と同等のecho "$(stuff)"
と同等である必要があります。コード全体の場合は、代わりにstuff
を実行します。
ただし、stuff
の出力を複数回使用する必要がある場合、このアプローチ
var=$(stuff)
foo "$var"
bar "$var"
通常は
foo "$(stuff)"
bar "$(stuff)"
foo
がecho
であり、コードでecho "$var"
を取得した場合でも、このように保持する方がよい場合があります。考慮事項:
var=$(stuff)
を使用すると、stuff
は1回実行されます。コマンドが高速であっても、同じ出力を2回計算しないようにするのが正しいことです。または、stuff
にはstdoutへの書き込み(たとえば、一時ファイルの作成、サービスの開始、仮想マシンの開始、リモートサーバーへの通知など)以外の効果があるため、複数回実行する必要はありません。stuff
が時間依存またはややランダムな出力を生成する場合、foo "$(stuff)"
およびbar "$(stuff)"
から一貫性のない結果が得られる可能性があります。 var=$(stuff)
の後、$var
の値は固定され、foo "$var"
とbar "$var"
が同じコマンドライン引数を取得することを確認できます。場合によっては、foo "$var"
の代わりにfoo $var
を使用する必要があります(特に、stuff
がfoo
に対してmultiple引数を生成する場合(配列変数はシェルがサポートしています)。繰り返しますが、あなたが何をしているかを知っています。 echo
に関しては、echo $var
とecho "$var"
の違いは、echo $(stuff)
とecho "$(stuff)"
の違いと同じです。
echo "$(stuff)"
はstuff
と同等(-ish)であると言いました。正確に同等ではない少なくとも2つの問題があります。
$(stuff)
はサブシェルでstuff
を実行するため、echo "$(stuff)"
は(stuff)
と同等(-ish)であると言う方が良いです。実行するシェルに影響するコマンドは、サブシェルの場合、メインシェルには影響しません。
この例では、stuff
はa=1; echo "$a"
です。
a=0
echo "$(a=1; echo "$a")" # echo "$(stuff)"
echo "$a"
と比較する
a=0
a=1; echo "$a" # stuff
echo "$a"
そして
a=0
(a=1; echo "$a") # (stuff)
echo "$a"
別の例では、stuff
がcd /; pwd
であることから始まります。
cd /bin
echo "$(cd /; pwd)" # echo "$(stuff)"
pwd
stuff
および(stuff)
バージョンをテストします。
echo
は、非制御データを表示するのに適したツールではありません 。私たちが話していたecho "$var"
はprintf '%s\n' "$var"
でなければなりませんでした。しかし、質問はecho
に言及しているため、最も可能性の高い解決策は、最初はecho
を使用しないことなので、これまでprintf
を導入しないことにしました。
stuff
または(stuff)
は、stdoutとstderrの出力をインターリーブし、echo $(stuff)
は、stuff
(最初に実行される)からのすべてのstderr出力を印刷し、その後echo
(最後に実行される)で消化されたstdout出力のみを印刷します。
$(…)
は末尾の改行を取り除き、echo
がそれを追加し直します。したがって、echo "$(printf %s 'a')" | xxd
はprintf %s 'a' | xxd
とは異なる出力を提供します。
一部のコマンド(ls
など)は、標準出力がコンソールであるかどうかによって動作が異なります。 ls | cat
はls
と同じではありません。同様に、echo $(ls)
はls
とは異なる動作をします。
ls
は別として、一般的な場合ifでこの他の動作を強制する必要がある場合、stuff | cat
はecho $(ls)
またはecho "$(ls)"
よりも優れています。 。
終了ステータスが異なる可能性があります(このwikiの回答の完全性について言及されています。詳細については、 別の回答 に値します)。
もう1つの違いは、サブシェルの終了コードが失われるため、代わりにecho
の終了コードが取得されることです。
> stuff() { return 1
}
> stuff; echo $?
1
> echo $(stuff); echo $?
0