「echo $(stuff)」または「echo `stuff`」の何が問題になっていますか?


31

次のいずれかを使用しました

echo $(stuff)
echo `stuff`

stuffたとえばpwddateまたは、もっと複雑なもの)。

それから私は、この構文は間違っている、悪い習慣、エレガントではない、過剰、冗長、過度に複雑、カーゴカルトプログラミング、noobish、ナイーブなどと言われました。

しかし、コマンド機能するので、正確に何が問題なのでしょうか?


11
冗長であるため、使用しないでください。:猫の無駄な使用と比較porkmail.org/era/unix/award.html
DR01

7
echo $(echo $(echo $(echo$(echo $(stuff)))))また、ない仕事を、まだあなたはおそらくそれを使用することはありません、右?;-)
ピーター-モニカの復職

1
まさにその拡張で何をしようとしているのですか?
curiousguy

@curiousguy私は他のユーザーを助けようとしているので、以下の私の答えです。最近、この構文を使用する少なくとも3つの質問を見ました。彼らの著者たちは…さまざまなことをしようとしていましたstuff。:D
カミルマシオロウスキ

2
また、反響室に閉じ込めることは賢明ではないと私たち全員が同意しませんか?? ;-)
ピーター-モニカの復活

回答:


63

tl; dr

ソールstuffおそらくあなたのために働くでしょう



完全な答え

何が起こるのですか

を実行するとfoo $(stuff)、次のようになります。

  1. stuff 実行;
  2. その出力(stdout)は、印刷される代わり$(stuff)に、foo; の呼び出しで置き換えられます。
  3. そして、fooそのコマンドライン引数は明らかに何かに依存して、実行されますstuff返さ。

この$(…)メカニズムは「コマンド置換」と呼ばれます。あなたの場合、メインコマンドはecho基本的にコマンドライン引数を標準出力に出力します。そのstuffため、stdoutに出力しようとするものはすべて、キャプチャされ、に渡さechoれ、stdoutに出力されechoます。

の出力stuffを標準出力に出力したい場合は、soleを実行してくださいstuff

`…`構文は同じ目的を果たし$(…)、あなたが盲目的にそれらを交換することはできませんので、いくつかの違いが、けれどもあります(「コマンド置換」と同じ名前で)。参照してください。このFAQこの質問を


echo $(stuff)何を避けてもいいですか?

echo $(stuff)自分が何をしているのかを知っているなら、あなたが使いたいと思う理由があります。同じ理由で、自分echo $(stuff)が何をしているのかわからない場合は避けるべきです。

ポイントはstuffecho $(stuff)まったく同じではありません。後者はstuff、デフォルト値の出力でsplit + glob演算子を呼び出すことを意味します$IFS。コマンド置換を二重引用符で囲むと、これが防止されます。コマンド置換を単一引用符で囲むと、コマンド置換ではなくなります。

分割に関してこれを観察するには、次のコマンドを実行します。

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)"はに相当します(-ish *)stuff。あなたはそれを使用することができますが、このように物事を複雑にすることのポイントは何ですか?

一方、あなたは、以下の場合にしたいの出力はstuff、あなたがグロブ+分割を受けることを見つけるecho $(stuff)便利。しかし、それはあなたの意識的な決定でなければなりません。

評価する必要のある出力(分割、グロビングなどを含む)を生成するコマンドがあり、シェルによって実行されるためeval "$(stuff)"、可能性があります(この回答を参照)。出力を追加してから印刷する前に追加の分割+グロビングを行う必要があるコマンドを見たことはありません。故意に使用することecho $(stuff)は非常に珍しいようです。


どうvar=$(stuff); echo "$var"

いい視点ね。このスニペット:

var=$(stuff)
echo "$var"

echo "$(stuff)"同等(-ish *)と同等でなければなりませんstuff。コード全体の場合は、stuff代わりに実行するだけです。

ただし、stuff複数回の出力を使用する必要がある場合は、このアプローチ

var=$(stuff)
foo "$var"
bar "$var"

通常は

foo "$(stuff)"
bar "$(stuff)"

たとえそうfooechoありecho "$var"、コードを取得したとしても、このように保つ方が良いかもしれません。考慮事項:

  1. var=$(stuff) stuff実行一度; コマンドが高速であっても、同じ出力を2回計算しないようにするのが正しいことです。またはstuff、stdoutへの書き込み以外の効果(一時ファイルの作成、サービスの開始、仮想マシンの開始、リモートサーバーへの通知など)があるため、複数回実行する必要はありません。
  2. stuff時間依存または多少ランダムな出力が生成される場合、foo "$(stuff)"およびから一貫性のない結果が得られる可能性がありbar "$(stuff)"ます。var=$(stuff)の値$varが修正された後、同じコマンドライン引数を確実foo "$var"bar "$var"取得できます。

場合によってfoo "$var"foo $var、特に複数の引数をstuff生成する場合(必要な場合)を使用する必要がありfooます(シェルがサポートしている場合は配列変数の方が適している場合があります)。繰り返しますが、あなたが何をしているかを知っています。それはになるとechoの間の差echo $varecho "$var"の間で同じであるecho $(stuff)echo "$(stuff)"


*同等(-ish)?

私は言ったecho "$(stuff)"に等しい(-ish)stuff。正確に同等ではない少なくとも2つの問題があります。

  1. $(stuff)stuffサブシェルで実行されるため、echo "$(stuff)"と同等(-ish)であると言う方が良い(stuff)です。サブシェルの場合、実行するシェルに影響するコマンドはメインシェルに影響しません。

    この例でstuffa=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)バージョン。

  2. echo非制御データを表示するのに適したツールではありませんecho "$var"私たちが話していたこれは、あるべきだったprintf '%s\n' "$var"。しかし、質問が言及echoしており、最も可能性の高いソリューションはそもそも使用echoしないことなので、私はこれまで紹介printfしないことにしました。

  3. stuffまたは(stuff)、stdoutとstderrの出力をインターリーブしecho $(stuff)stuff(最初に実行される)からすべてのstderr出力を出力し、次にecho(最後に実行される)によって消化されたstdout出力のみを出力します。

  4. $(…)後続の改行を取り除き、それechoを元に戻します。そのため、とecho "$(printf %s 'a')" | xxdは異なる出力が得られますprintf %s 'a' | xxd

  5. 一部のコマンド(lsたとえば)は、標準出力がコンソールかどうかによって動作が異なります。これls | catと同じではありませんlsありません。同様echo $(ls)にの動作も異なりlsます。

    置くls一般的なケースには、さておき場合は、あなたがこの他の動作を強制する必要があり、その後stuff | catよりも優れているecho $(ls)echo "$(ls)"、それは他のすべての問題は、ここで言及トリガされませんので。

  6. 終了ステータスが異なる可能性があります(このwikiの回答の完全性について言及されています。詳細については、評価に値する別の回答を参照してください)。


5
もう1つの違い:stuff方法はインターリーブstdoutしてstderr出力しますが、からのecho `stuff` すべてのstderr出力を出力しstuff、その後にstdout出力のみを出力します。
ルスラン

3
そして、これは別の例です。bashスクリプトに引用符が多すぎることはほとんどありません。グロブや拡張を明示的に望んでいないかのようecho $(stuff)に、はるかに多くのトラブルに巻き込まれるecho "$(stuff)"可能性があります。
rexkogitans

2
もう1つわずかな違いがあり$(…)ますecho。末尾の改行を削除してから追加します。そのため、とecho "$(printf %s 'a')" | xxdは異なる出力が得られますprintf %s 'a' | xxd
デロバート

1
ls標準出力がコンソールであるかどうかに応じて、一部のコマンド(たとえば)が異なる動作をすることを忘れないでください。これls | catと同じではありませんlsありません。そしてもちろん、のecho $(ls)動作とは異なりlsます。
マーティンローゼ

@Ruslan現在、答えはコミュニティwikiです。あなたの便利なコメントをwikiに追加しました。ありがとうございました。
カミルマシオロウスキ

5

別の違い:サブシェルの終了コードが失われるため、echo代わりにの終了コードが取得されます。

> stuff() { return 1
}
> stuff; echo $?
1
> echo $(stuff); echo $?
0

3
スーパーユーザーへようこそ!実証している違いを説明できますか?ありがとう
-bertieb

4
これは、戻りコード$?がedによるものではなくecho(for echo $(stuff))の戻りコードになることを示していると思います。関数(終了コード)が、関係なく、戻りコードの「0」に設定すること。まだ答えがあるはずです。returnstuffstuffreturn 1echostuff
イスマエルミゲル

サブシェルからの戻り値が失われました
phuclv
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.