Bashの$(コマンド置換)内での引用


126

Bash環境では、スペースを含む変数を使用し、これらの変数をコマンド置換内で使用します。残念ながら、SEで答えが見つかりません。

変数を引用する正しい方法は何ですか?そして、これらがネストされている場合はどうすればよいですか?

DIRNAME=$(dirname "$FILE")

または、置換外で引用しますか?

DIRNAME="$(dirname $FILE)"

または両方?

DIRNAME="$(dirname "$FILE")"

またはバックティックを使用しますか?

DIRNAME=`dirname "$FILE"`

これを行う正しい方法は何ですか?そして、引用符が正しく設定されているかどうかを簡単に確認するにはどうすればよいですか?




1
これは良い質問ですが、埋め込みブランクに関するすべての問題を考えると、それらを意図的に使用することで自分を苦しめるのはなぜですか?
ジョー14年

3
@Joe、空白が埋め込まれていると、ファイル名にスペースが必要ですか?個人的にはあまり使用しませんが、他の人のディレクトリやファイルで作業しており、それらにスペースが含まれているかどうかはわかりません。さらに、私はそれをすぐに正しくする方が良いと思うので、将来心配する必要はありません。
いとこコカイン14年

4
はい。私たちはそれらの「他の人」と何をするつもりですか?<G>
ジョー14年

回答:


188

最悪から最高の順に:

  • DIRNAME="$(dirname $FILE)" あなたがやりたいことはありません場合は$FILE、空白またはグロブの文字が含まれています\[?*
  • DIRNAME=`dirname "$FILE"`技術的には正しいですが、バックティックはネストするときに余分な複雑さがあるため、コマンド拡張には推奨されません
  • DIRNAME=$(dirname "$FILE")これは正しいですが、これは割り当てであるためです。export DIRNAME=$(dirname "$FILE")またはなどの他のコンテキストでコマンド置換を使用する場合du $(dirname "$FILE")、展開の結果に空白またはグロビング文字が含まれていると、引用符がないと問題が発生します。
  • DIRNAME="$(dirname "$FILE")"推奨される方法です。DIRNAME=何も変更せずにコマンドとスペースに置き換えることができdirname、正しい文字列を受け取ります。

さらに改善するには:

  • DIRNAME="$(dirname -- "$FILE")"$FILEダッシュで始まる場合に機能します。
  • DIRNAME="$(dirname -- "$FILE"; printf x)" && DIRNAME="${DIRNAME%?x}"働く$FILEので、改行で終わる$()出力の最後にチョップオフ改行とdirname結果の後に改行を出力します。シーシュdirname、どうして違うの?

コマンド展開は好きなだけネストできます。では$()あなたは常に新しい引用コンテキストを作成するので、あなたは、このようなことを行うことができます。

foo "$(bar "$(baz "$(ban "bla")")")"

バッククティックでそれを試してみたいとは思わないでしょう。


3
私の質問に対する明確な答え。これらの変数をネストすると、今のように引用を続けることができますか?
いとこコカイン14年

3
引用符内のコマンド置換内の引用符の動作を詳述する参照/リソースはありますか?
-AmadeusDrZaius

12
@AmadeusDrZaius「$()常に新しい引用コンテキストを作成します」ので、外側の引用符の外側のようになります。私の知る限り、これ以上のものはありません。
-l0b0

4
@ l0b0ありがとう、ええ、あなたの説明は非常に明確でした。どこかにマニュアルが入っているのかと思っていました。wooledgeで(非公式ではありますが)見つけました。置換の順序について注意深く読んだ場合、結果としてこの事実を導き出せると思います。
-AmadeusDrZaius

1
そのため、ネストされた引用符は受け入れられますが、ほとんどの構文カラーリングスキームは特別な状況を検出しないため、引用符は無視されます。きちんとした。
ルークデイビス

19

を使用すると、変数の引用の効果をいつでも表示できますprintf

単語分割は次の場所で行われvar1ます。

$ var1="hello     world"
$ printf '[%s]\n' $var1
[hello]
[world]

var1 引用符で囲まれているため、単語の分割はありません:

$ printf '[%s]\n' "$var1"
[hello     world]

var1内部$()での単語分割、次と同等echo "hello" "world"

$ var2=$(echo $var1)
$ printf '[%s]\n' "$var2"
[hello world]

単語の分割はありませんvar1、引用符を付けなくても問題ありません$()

$ var2=$(echo "$var1")
$ printf '[%s]\n' "$var2"
[hello     world]

var1再び単語分割:

$ var2="$(echo $var1)"
$ printf '[%s]\n' "$var2"
[hello world]

両方を引用して、最も簡単な方法を確認してください。

$ var2="$(echo "$var1")"
$ printf '[%s]\n' "$var2"
[hello     world]

グロビングの問題

変数を引用しないと、その内容のグロブ展開につながる可能性があります。

$ mkdir test; cd test; touch file1 file2
$ var="*"
$ printf '[%s]\n' $var
[file1]
[file2]
$ printf '[%s]\n' "$var"
[*]

これは、変数が展開された後にのみ発生することに注意してください。割り当て中にグロブを引用する必要はありません。

$ var=*
$ printf '[%s]\n' $var
[file1]
[file2]
$ printf '[%s]\n' "$var"
[*]

set -fこの動作を無効にするために使用します。

$ set -f
$ var=*
$ printf '[%s]\n' $var
[*]

そしてset +f、再度有効にするには:

$ set +f
$ printf '[%s]\n' $var
[file1]
[file2]

8
単語の分割だけが問題ではないことを人々は忘れがちvar1='hello * world'です。グロビングの問題を説明するために例を変更することもできます。
ステファンシャゼル14年

10

受け入れられた答えへの追加:

私はここで@ l0b0の答えに一般的に同意しますが、「最悪から最高」のリストに裸のバックティックを配置することは、少なくとも部分的に$(...)どこでも利用可能な仮定の結果であると思います。質問ではBashを指定していることを認識していますが、Bashがmean /bin/shになっている場合が多くあります。

特に、プレーンなBourneシェルはの処理方法を知らない$(...)ので、それと互換性があると主張するスクリプト(たとえば、#!/bin/shシバンラインを介して)は、実際に「本物」によって実行されると誤動作する可能性/bin/shがあります。たとえば、initスクリプトを作成したり、プリスクリプトとポストスクリプトをパッケージ化したり、ベースシステムのインストール中に驚くべき場所にそれを置くことができる場合に特に関心があります。

この変数を使用して何かを計画しているように思える場合は、スクリプトを実際に予測可能に実行するよりも、ネストの方がおそらく問題になりません。それが十分に単純なケースであり、移植性が懸念される場合、スクリプトが通常/bin/sh Bashのシステムで実行されることを期待している場合でも、ネストの代わりに複数の割り当てで、この理由でバックティックを使用する傾向があります。

とはいえ、実装するシェルの普遍性$(...)(Bash、Dashなど)は、ほとんどの場合、よりきれいで、入れ子になりやすく、最近好まれたPOSIX構文に固執する良い場所に私たちを置いています。 @ l0b0が言及するすべての理由。

余談:これはStackOverflowでも時々表示されます–


//、すばらしい回答。私も過去に/ bin / shとの後方互換性の問題に遭遇しました。後方互換性のある方法を使用して彼の問題に対処する方法についてアドバイスはありますか?
ネイサンバサネーゼ

私の経験では、バッククォートをドル記号に変更することが必要な場合がありますが、バッククォートに含まれるドル記号を変更するのに(特定の理由で)一度も助けたことがありません。バックコードはJavaScriptコードでのみ記述し、シェルコードでは記述しません。
スティーブンルー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.