シェルスクリプトでバッククォートの代わりに$()を使用する利点は何ですか?


175

コマンドラインの出力をキャプチャする方法は2つありますbash

  1. レガシーボーンシェルのバックティック``

    var=`command`
  2. $() 構文(私が知る限り、これはBash固有であるか、少なくとも元のBourneのような非POSIX古いシェルではサポートされていません)

    var=$(command)

バックティックと比較して2番目の構文を使用する利点はありますか?それとも2つは完全に100%同等ですか?


14
$()POSIXであり、ksh、bash、ash、dash、zsh、busyboxなどのすべての最新のBourneシェルでサポートされています。(それほどモダンではない/bin/shがSolarisですが、Solaris /usr/xpg4/bin/shでは代わりにモダンを使用することを確認します)。
イェンス、

27
また、$()エイリアスでの使用とバッククォートに関する注意。あなたが持っている場合はalias foo=$(command)、あなたの中で.bashrc、その後commandaliasコマンド自体が中に実行されたときに実行されます.bashrc解釈。でalias foo=`command`commandエイリアスが実行されるたびに実行されます。ただし$$()フォーム(例:)でエスケープすると、解釈alias foo=\$(command)中ではなく、エイリアスが実行されるたびに実行.bashrcされます。とにかく、テストでわかる限り、この動作を説明するbashドキュメントには何も見つかりません。
ダートサイド2014

4
@dirtsideどのシェルがこれであるか、私はbashとPOSIXシェルをテストしました、私がソースするときにバックティックは実行されます。簡単な例:エイリアスcurDate = `date`ソースしてcurDateを実行した後、たとえば、Mon(Mondaydにソース)というコマンドが見つからないというメッセージが表示されます。
thecarpy

@dirtsideそれは真実ではありません。エイリアスfoo =を使用しても、`command` command1回だけ実行されます。私はそれをチェックしました:function aaa(){printf date; echo aaa >>〜/ test.txt; エイリアスtest1 = aaa。エイリアス(test1)が何回実行されても、関数aaaは1回だけ(ログインごとに)実行されます。.bashrc(Debian 10)を使用しました。
vitaliydev

わからない、それは私が5年半前に使用していたbashのバージョンであり、おそらくその時点でさえかなり古いものでした。私のテストが正しくなかった可能性がありますが、誰もまだどのバージョンでも使用しているとは思えないので、今ではほとんど問題になりません。
ダートサイド

回答:


156

主なものは、何らかの形のエスケープがバックティックで機能するかどうかを判断しようとする正気を失うことなく、それらをコマンド内にネストする機能です。

やや工夫はされていますが、例:

deps=$(find /dir -name $(ls -1tr 201112[0-9][0-9]*.txt | tail -1l) -print)

これにより、/dir2011年12月以降の最も古い日付のテキストファイルと同じ名前を持つ、ディレクトリツリー内のすべてのファイルのリストが表示されます(a)

別の例は、親ディレクトリの名前(フルパスではない)を取得するようなものです。

pax> cd /home/pax/xyzzy/plugh
pax> parent=$(basename $(dirname $PWD))
pax> echo $parent
xyzzy

()今で特定のコマンドは、実際の仕事、私は機能をテストしていないかもしれません。ですから、私に反対票を投じると、意図が見えなくなってしまいます:-)これは、ネスト可能な方法に関する単なる実例であり、バグのない本番用のスニペットではありません。


86
私は、SO上のすべてのコードが、NASAシャトルコードの信頼性基準に基づいて設計された、プロダクションレディスニペットであることを期待しています。それより少ないものはフラグと削除投票を取得します。
DVK、2012

1
@DVK冗談ではない場合、そのような保証または目的の適合性を可能にするために、SOのデフォルト(CC-BY-SAまたはMITライセンス)から自動的に再ライセンスできない場合は、コード提供にフラグを立てるべきではないことに同意します。私は等の有用性、技術的なメリットによれば、代わりに自分自身の責任でSO上のコード再利用のだろう、と投票貢献
chrstphrchvz

9
@chrstphrchvz、私のプロファイルを見ると、次の小さなスニペットが表示されますDo whatever the heck you want with it.。 :-)いずれにせよ、私はそれがDVKからのユーモアだったと確信しています。
paxdiablo 2017

1
「cd / home / pax / xyzzy / plover」の場合はどうでしょうか?曲がりくねった小さな通路の迷路に身を置きますか?
ダンカン

@wcharginは、このインシデントが使用済みの定義済みリテラルをC ++言語に追加する主要な動機であることを知っていましたか?en.cppreference.com/w/cpp/language/user_literal
ThomasMcLeod

59

gccがインストールされている場所に対応するlibディレクトリを検索するとします。あなたには選択肢があります:

libdir=$(dirname $(dirname $(which gcc)))/lib

libdir=`dirname \`dirname \\\`which gcc\\\`\``/lib

1つ目は2つ目よりも簡単です-最初のものを使用してください。


4
これらのコマンド置換を引用符で囲んでおくとよいでしょう。
トムフェネック2017

1
少なくともbashの場合、@ TomFenechのコメントは割り当てには適用されません。x=$(f); x=`f` と同じように動作しx="$(f)"; x="`f`"ます。対照的に、配列の割り当てx=($(f)); x=(`f`) $IFSコマンドを呼び出すときに期待どおりに文字で分割を実行します。これは便利です(x=1 2 3 4意味がありません)が、一貫性がありません。
kdb

@kdb x=$(f)引用符なしの作業について正しい。私はもっ​​と具体的だったはずです。私は使用を提案していましたlibdir=$(dirname "$(dirname "$(which gcc)")")/lib内部のコマンド置換を囲む引用符)。引用符で囲まれていない場合でも、通常の単語分割とグロブ拡張の影響を受けます。
トムフェネック

38

バックティック(`...`)は、非常に古い非POSIX互換のボーンシェルでのみ必要とされるレガシー構文であり、$(...)POSIXであり、いくつかの理由でより好まれています。

  • バックティック\内のバックスラッシュ()は、明白ではない方法で処理されます。

    $ echo "`echo \\a`" "$(echo \\a)"
    a \a
    $ echo "`echo \\\\a`" "$(echo \\\\a)"
    \a \\a
    # Note that this is true for *single quotes* too!
    $ foo=`echo '\\'`; bar=$(echo '\\'); echo "foo is $foo, bar is $bar" 
    foo is \, bar is \\
  • 内部のネストされた引用$()ははるかに便利です:

    echo "x is $(sed ... <<<"$y")"

    の代わりに:

    echo "x is `sed ... <<<\"$y\"`"

    または次のようなものを書く:

    IPs_inna_string=`awk "/\`cat /etc/myname\`/"'{print $1}' /etc/hosts`

    $()引用にまったく新しいコンテキストを使用するため

    BourneシェルとKornシェルはこれらのバックスラッシュを必要とするため、移植性はありませんが、Bashとdashは必要ありません。

  • コマンド置換をネストするための構文はより簡単です:

    x=$(grep "$(dirname "$path")" file)

    より:

    x=`grep "\`dirname \"$path\"\`" file`

    なぜなら$()、引用のための完全に新しいコンテキストを適用するため、各コマンド置換は保護され、引用とエスケープに関する特別な懸念なしに、それ自体で処理できます。バックティックを使用すると、2つ以上のレベルの後で醜くなります。

    いくつかの例:

    echo `echo `ls``      # INCORRECT
    echo `echo \`ls\``    # CORRECT
    echo $(echo $(ls))    # CORRECT
  • 逆引用符を使用する場合の一貫性のない動作の問題を解決します。

    • echo '\$x' 出力 \$x
    • echo `echo '\$x'` 出力 $x
    • echo $(echo '\$x') 出力 \$x
  • バッククォートの構文は、埋め込みコマンドの内容に歴史的な制限があり、バッククォートを含む一部の有効なスクリプトを処理できませんが、新しい$()フォームでは、あらゆる種類の有効な埋め込みスクリプトを処理できます。

    たとえば、これらのその他の有効な埋め込みスクリプトは左側の列では機能しませんが、右側のIEEEでは機能します。

    echo `                         echo $(
    cat <<\eof                     cat <<\eof
    a here-doc with `              a here-doc with )
    eof                            eof
    `                              )
    
    
    echo `                         echo $(
    echo abc # a comment with `    echo abc # a comment with )
    `                              )
    
    
    echo `                         echo $(
    echo '`'                       echo ')'
    `                              )

したがって、$-prefixed コマンド置換の構文は、明確な構文で視覚的に明確であり(人間とマシンの可読性が向上)、ネスト可能で直感的であり、内部の解析が分離されており、一貫性も高いため(推奨)二重引用符内から解析される他のすべての展開)バッククォートが唯一の例外であり、特に小さいフォントや珍しいフォントで`は、隣接するときに文字が簡単にカモフラージュさ"れ、読みにくくなります。

出典:(バックティック)より$(...)優先される理由 `...`BashFAQで

以下も参照してください。


1
アクセントグラビススタイルの置換でのネストされた引用は実際には未定義です。二重引用符外側で内側で使用できますが、両方では使用できません。シェルはそれらを異なる方法で解釈します。それらをエスケープするためにバックスラッシュが必要なものもあれば、バックスラッシュでエスケープされていないことが必要なものもあります。
mirabilos

23

man bashから:

          $(command)
   or
          `command`

   Bash performs the expansion by executing command and replacing the com-
   mand  substitution  with  the  standard output of the command, with any
   trailing newlines deleted.  Embedded newlines are not deleted, but they
   may  be  removed during word splitting.  The command substitution $(cat
   file) can be replaced by the equivalent but faster $(< file).

   When the old-style backquote form of substitution  is  used,  backslash
   retains  its  literal  meaning except when followed by $, `, or \.  The
   first backquote not preceded by a backslash terminates the command sub-
   stitution.   When using the $(command) form, all characters between the
   parentheses make up the command; none are treated specially.

9

他の答えに加えて、

$(...)

より視覚的に優れています

`...`

バックティックはアポストロフィのように見えます。これは、使用しているフォントによって異なります。

(そして、私が気付いたように、バックコードはインラインコードサンプルに入力するのがはるかに困難です。)


2
あなたは奇妙なキーボードを持っている必要があります(または私はそうですか?)。私にとっては、バックティックを入力する方がはるかに簡単です。これらは左上隅のキーであり、ShiftキーやAltキーは必要ありません。
DVK、2012

1
@DVK:私はタイピングの容易さではなく、彼らの外見について話していました。(私のキーボードはおそらくあなたのものと同じです。)それでも、あなたがそれを言ったので、私は私のために$ (、そして)バックティックのために私よりも良い筋肉の記憶を持っていると思います。YMMV。
キース・トンプソン、

bashでプログラムされていない(古いkshからPerlにスキップされた)ため、特定の構文ではメモリがまったくない:)
DVK

1
@DVK、私はキースがここの非ブロックコード(ブロックコードは行の先頭に4つのスペースを使用することを意味する)がそれを示すためにバックティックを使用するという事実を参照していることを言及していると思ったネストの難しさ:-) FWIW、コードと/ codeタグ(非ブロックコードを実行する別の方法)がバッククォートをより簡単に含むことができる場合があります。
paxdiablo

@Pax-わかった。ああ!確かに私は何らかの理由でブロックコードに心を動かされていました。
DVK、2012

7

$() 入れ子にすることができます。

out=$(echo today is $(date))

バックティックはそれを許可しないと思います。


2
バックティックをネストできます。それははるかに難しいですout=`echo today is \`date\``
ジョナサンレフラー、2015

4

$(command)コマンド置換の形式を定義するのはPOSIX標準です。現在使用されているほとんどのシェルはPOSIXに準拠しており、古風なバックティック表記よりもこの優先形式をサポートしています。シェル言語ドキュメントのコマンド置換セクション(2.6.3)でこれについて説明しています。

コマンド置換では、コマンド名自体の代わりにコマンドの出力を置換できます。コマンドが次のように囲まれている場合、コマンド置換が発生します。

$(command)

または(逆引用バージョン):

`command`

実行することで、コマンド置換を拡大するものとシェルコマンドを サブシェル環境では、(参照のシェルの実行環境とコマンド置換(のテキスト置き換え)コマンド削除、コマンドの標準出力をプラス囲む「$()」またはバッククォートを)<newline>置換の最後にある1つ以上の文字のシーケンス。埋め込み<newline>出力の最後の前に文字は削除されません。ただし、IFSの値と有効な引用に応じて、フィールド区切り文字として扱われ、フィールド分割時に削除される場合があります。出力にnullバイトが含まれている場合の動作は規定されていません。

逆引用符で囲まれたスタイルのコマンド置換で<backslash>$、 ' `'、' '、またはが 後に続く場合を除いて、その文字どおりの意味を保持し<backslash>ます。一致する逆引用符の検索は、最初の引用符で囲まれていないエスケープされていない逆引用符によって満たされます。この検索中に、シェルコメント、ヒアドキュメント、$(command)形式の埋め込みコマンド置換、または引用文字列内でエスケープされていないバッククォートが検出されると、未定義の結果が発生します。" `...`"シーケンス内で開始するが終了しない一重引用符または二重引用符付きの文字列は、未定義の結果を生成します。

$(command)形式では、左括弧とそれに続く右括弧の後に続くすべての文字がcommandを構成し ます。有効でないシェルスクリプトをcommandに使用できますが、指定されていない結果を生成するリダイレクトのみで構成されるスクリプトは例外です。

コマンド置換の結果は、さらなるチルド拡張、パラメーター拡張、コマンド置換、または算術拡張のために処理されません。コマンドの置換が二重引用符内で行われる場合、置換の結果に対してフィールド分割とパス名展開は実行されません。

コマンド置換はネストできます。バッククォートされたバージョン内でのネストを指定するには、アプリケーションは内部のバッククォートの前に<backslash>文字を付けなければなりません。例えば:

\`command\`

シェルコマンド言語の構文には、 "で始まる展開があいまいです。$(("、これはサブシェルで始まる算術展開またはコマンド置換を導入できます。算術展開が優先されます。つまり、シェルはまず展開を算術展開として解析できるかどうかを判断し、展開をコマンドとしてのみ解析します算術展開として展開を解析できないと判断した場合、置換。シェルは、この決定を実行するときにネストされた展開を評価する必要はありません。展開を算術展開として解析できないとまだ判断せずに入力の終わりに遭遇した場合、シェルは、展開を不完全な算術展開として扱い、構文エラーを報告します。準拠するアプリケーションは、「$(」と「('サブシェルで始まるコマンド置換で、2つのトークンに(つまり、空白で区切ります)。たとえば、単一のサブシェルを含むコマンド置換は、次のように書くことができます。

$( (command) )


4

これは古い質問ですが、私は完全に有効な$(...)overの例を思いつきました`...`

cygwinを実行しているウィンドウにリモートデスクトップを使用していて、コマンドの結果を繰り返し処理したいと思っていました。悲しいことに、リモートデスクトップの問題またはcygwin自体のせいで、バックティック文字を入力できませんでした。

このような奇妙な設定では、ドル記号と括弧がタイプしやすいと考えるのは常識です。

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