web-dev-qa-db-ja.com

bashでワイルドカード/アスタリスク文字をエスケープするにはどうすればよいですか?

例えば。

me$ FOO="BAR * BAR"
me$ echo $FOO
BAR file1 file2 file3 file4 BAR

そして、「\」エスケープ文字を使用します:

me$ FOO="BAR \* BAR"
me$ echo $FOO
BAR \* BAR

私は明らかに愚かなことをしています。

出力「BAR * BAR」を取得するにはどうすればよいですか?

123
andyuk

$ FOOを設定するときに引用するだけでは不十分です。変数参照も引用符で囲む必要があります。

me$ FOO="BAR * BAR"
me$ echo "$FOO"
BAR * BAR
121
finnw

短い答え

他の人が言ったように-あなたはいつも変な振る舞いを防ぐために変数を引用すべきです。そのため、echo $ fooの代わりにecho "$ foo"を使用します。

長い回答

この例では、一見思われる以上のことが行われているため、さらに説明する必要があると思います。

あなたが最初の例を実行した後、おそらくシェルが明らかにしていると自分自身に考えたので、あなたの混乱がどこに来るかを見ることができます:

  1. パラメータ展開
  2. ファイル名展開

最初の例から:

me$ FOO="BAR * BAR"
me$ echo $FOO

パラメータ展開後は次と同等です。

me$ echo BAR * BAR

そして、ファイル名展開後は次と同等です:

me$ echo BAR file1 file2 file3 file4 BAR

そして、単にecho BAR * BARをコマンドラインに入力すると、それらが同等であることがわかります。

あなたはおそらく「*をエスケープすれば、ファイル名の展開を防ぐことができる」と考えたでしょう。

2番目の例から:

me$ FOO="BAR \* BAR"
me$ echo $FOO

パラメータの展開後は次と同等になります。

me$ echo BAR \* BAR

また、ファイル名の展開後は次のようになります。

me$ echo BAR \* BAR

また、コマンドラインに「echo BAR\* BAR」と直接入力しようとすると、エスケープによってファイル名の展開が妨げられるため、実際には「BAR * BAR」が出力されます。

では、なぜ$ fooを使用してもうまくいかなかったのでしょうか?

これは、3番目の拡張であるQuote Removalがあるためです。 bashからの手動引用の削除は次のとおりです。

上記の展開の後、上記の展開のいずれにも起因しない文字「\」、「 '」、および「 "」の引用符で囲まれていない出現はすべて削除されます。

したがって、コマンドラインにコマンドを直接入力すると、エスケープ文字は以前の展開の結果ではないため、BASHはechoコマンドに送信する前にそれを削除しますが、2番目の例では、「\ *」は前回のパラメーター展開の結果であるため、削除されません。その結果、エコーは「\ *」を受信し、それが出力されます。

最初の例との違いに注意してください-「*」は、引用の削除によって削除される文字には含まれません。

これが理にかなっていることを願っています。最後に同じで結論-引用符を使用してください。パラメータとファイル名の展開のみが機能している場合に論理的に機能するエスケープが機能しなかった理由を説明したいと思いました。

BASH拡張の詳細な説明については、以下を参照してください。

http://www.gnu.org/software/bash/manual/bashref.html#Shell-Expansions

96
mithu

この古いスレッドに少し追加します。

通常は使用します

$ echo "$FOO"

ただし、この構文でも問題が発生しました。次のスクリプトを検討してください。

#!/bin/bash
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1"

*curlに逐語的に渡す必要がありますが、同じ問題が発生します。上記の例は機能せず(現在のディレクトリのファイル名に展開されます)、\*も機能しません。 curlに対する単一の(無効な)オプションとして認識されるため、$curl_optsも引用できません。

curl: option -s --noproxy * -O: is unknown
curl: try 'curl --help' or 'curl --manual' for more information

したがって、bash変数$GLOBIGNOREを使用して、グローバルパターンにファイル名の展開を完全に適用しないようにするか、set -f組み込みフラグを使用することをお勧めします。

#!/bin/bash
GLOBIGNORE="*"
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1"  ## no filename expansion

元の例に適用する:

me$ FOO="BAR * BAR"

me$ echo $FOO
BAR file1 file2 file3 file4 BAR

me$ set -f
me$ echo $FOO
BAR * BAR

me$ set +f
me$ GLOBIGNORE=*
me$ echo $FOO
BAR * BAR
47
AlexandreH
FOO='BAR * BAR'
echo "$FOO"
4
tzot

コマンドラインでprintfではなくechoを使用する習慣を身に付ける価値があるかもしれません。

この例では、あまり利点はありませんが、より複雑な出力でより有用になる可能性があります。

FOO="BAR * BAR"
printf %s "$FOO"
3
Dave Webb
echo "$FOO"
3
Rafał Dowgird