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


136

例えば:

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

\エスケープ文字を使用する:

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

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

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

回答:


139

設定$FOOが不十分な場合の引用。変数参照も引用する必要があります。

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

8
これは神秘的です、なぜこれはなぜですか?何が起こっている?
tofutim 2018年

変数が拡大するため
ダニエル

103

短い答え

他の人が言ったように-変な振る舞いを防ぐために常に変数を引用するべきです。したがって、単に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番目の拡張が行われるためです-引用の削除。bashからの手動引用の削除は次のとおりです。

上記の展開の後、上記の展開のいずれかによって生成されなかった文字「\」、「」、および「」の引用符で囲まれていないすべての出現が削除されます。

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

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

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

BASH展開の詳細については、以下を参照してください。

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


1
正解です。今、私はそんな馬鹿な質問をした気がしません。:-)
アンデューク2008

1
特殊文字をエスケープするユーティリティがいくつかありますか?
Jigar Joshi 2012

「変な動作を防ぐために、常に変数を引用する必要があります」-それらを文字列として使用したい場合
Angelo


上記の@finnwからの回答は質問に直接回答しますが、この回答はその理由を説明するのにはるかに優れています。これは、私たちがもっと見る必要がある種類の答えです。
Nicolas Coombs

54

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

通常は使用します

$ echo "$FOO"

ただし、この構文でも問題が発生しました。次のスクリプトを考えてみましょう。

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

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

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

4
素晴らしい説明、ありがとう!私のユースケースはですSELECT * FROM etc.。これが機能する唯一の方法です。
knutole 2015

1
set -fソリューションをご提示いただきありがとうございます。
hachre 2017年

5
FOO='BAR * BAR'
echo "$FOO"

これは機能しますが、最初の行の単一引用符を二重引用符に変更する必要はありません。
finnw 2008

1
いいえ、そうではありませんが、特殊なシェル文字を含めて置換をしたくない場合は、一重引用符を使用するのが望ましい方法です。
tzot 2008


3

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

この例では、あまりメリットはありませんが、より複雑な出力の方が便利です。

FOO="BAR * BAR"
printf %s "$FOO"

なぜ地獄に?printfは独立したプロセスであり(少なくとも、bashに組み込まれていません)、実証するprintfを使用しても、エコーに勝る利点はありません。
ddaa 2008

2
printf関数はある。bashで内蔵し、私はそれがはるかに利益を与えるものではありませんが、それは、より複雑な出力とより便利です。この例では、」私の答えに言った
デイブウェッブ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.