bashで感嘆符(!)を使用できませんか?


87

curlコマンドを使用!して、パスに感嘆符()が付いたhttp URLにアクセスしようとしています。例えば:

curl -v "http://example.org/!287s87asdjh2/somepath/someresource"

コンソールはで応答しbash: ... event not foundます。

ここで何が起こっていますか?感嘆符をエスケープするための適切な構文は何でしょうか?


bash 4.4+で解決
Isaac

回答:


99

感嘆符は、bashの履歴展開の一部です。これを使用するには、単一引用符で囲む必要があります(例:)、'http://example.org/!132'または\文字の前にバックスラッシュ()を付けて直接エスケープする必要があります(例:)"http://example.org/\!132"

二重引用符では、エクスクラムの前のバックスラッシュが履歴の展開を妨げますが、そのような場合、バックスラッシュは削除されないことに注意してください。したがってcurl、URLの一部としてリテラルのバックスラッシュを渡さないように、単一引用符を使用することをお勧めします。


8
"http://example.org/\!132"バックスラッシュを解釈せずに実際に展開します(POSIX準拠の理由、私は信じています)。
クリスダウン

@ChrisDown、テキストの2番目のオプションであることを明確にしようとしました。混乱の可能性を指摘してくれてありがとう。
ダニエルピットマン

6
記録用:「!」をエスケープしてみることは移植性がありません。ベストプラクティスの推奨事項は、常に "!"を引用符(単一引用符)で囲むことです。関連: "^"(キャレット)は、移植性のために引用符で囲む必要がある非メタ文字です。最後に、 "!" ifステートメントでは使用しないでください。可能であれば、代わりにテスト用の引数として使用してください(これもSolarisの/ bin / shによる)。
ニコラスウィルソン

5
単一引用符だけが私のために働いた。zshはまだ解釈\!と二重引用符でした。
オルコデン

1
Solaris(XPG4以前の古い古いシェル)では、「^」はエイリアスで|あり、パイプの作成に使用されます。スクリプトを顧客に送信していて、どのシェルで実行されるかわからない場合は、すべてをテストする必要があります!
ニコラスウィルソン

61

ダニエルの答えに加えて、を使用しない場合は、単に履歴展開を完全にオフにすることもできますset +H


19
履歴の拡張を完全にオフにすることは、私が一日中聞いた中で最高のアドバイスです!履歴展開は危険であるビザンチンがあるときより良い選択肢(と増分履歴検索Ctrl-Rあなたはプレビュー&あなたが盲目的にコマンドを離れて発生しませんので、あなたのコマンドを編集してみましょう)!-14あなたががにあった!-12、おっと、あることを起こっていますrm -rf *。安全である。履歴展開を無効にします!避けて!ください!
aculich

6
最大の答え:履歴の拡大は大きなセキュリティリスクです!細工されたURLを介してUnixを攻撃するために使用できます。
ダン

@aculich、または代わりにPOSIX指定のコマンドをfc -14使用してください。 しかし、履歴展開も有効にせずにそれを行うことができるのは事実です。個人的に、私が使用!$して!visudo !!しても、git add !vi:$多くの場合、十分な履歴展開が有効になったまま保証します。
ワイルドカード

これをシェルRCファイルに追加すると思います。私はこれをきちんとした「トリック」として使用したことがあります
TonyH

17

私は個人的に単一引用符を行うだろうが、完全を期すために、私はまた、それがURLであるため、あなたがエンコードすることができます注意!として%21例えば、curl -v http://example.org/%21132


13

これもできます

curl -v "http://example.org/"'!'"287s87asdjh2/somepath/someresource"
または
curl -v "http://example.org/"\!"287s87asdjh2/somepath/someresource"

これは、bashが隣接する文字列を連結するために機能します。このアプローチは、シェルの展開を必要とする他のものがある場合に特に役立ちます。したがって、文字列全体に一重引用符を使用することはできません。

curl -v 'http://example.org/!'"287s87asdjh2/${basepath}/someresource"

!文字は、コマンドラインプロンプトでの履歴展開に使用されます。
そのため、これはプロンプトでは問題になりますが、シェルスクリプトファイルでは問題になりません。
履歴の展開は二重引用符でも機能することがわかります。


Unixのコマンドや英語の文に必要以上の文字を使用させ、必要以上に混乱させる方法はたくさんあります。これは、最初の/受け入れられた/最も投票された回答、つまりURL全体を一重引用符で囲むよりも優れていますか?
Gマン

2
@ G-Man:bash引数を作成する別の方法を示します。私はこの方法を知りませんでした。新しいことを学ぶのに何も悪いことはありません。
サヒルシン

@SahilSinghこれはどうですか?2つの二重引用符と1つの単一引用符で囲まれた3つの文字列を連結します。ここにはネストはありません。
ラファエル

@ G-Man 2つの文字列を隣同士に並べると、それらが連結されることは明らかではありません。printf( "hello" "world")はcでも機能しますが、printf( "hello" 'w')は機能しません。そのため、bashがそのような表現に対応していることは私にとっては新しいことでしたが、実用的な観点から、これは優れていません。私は答えが好きだったので、マーク・シャストもそうでした。
サヒルシン

2
@ G-Man 同じ文字列で他の文字列展開必要な場合にも便利です。これは、2種類の引用動作を簡単に分離する方法です。
WAF

7

私は同じ問題に遭遇しましたが、私の簡単な解決策は変数を使用することでした:

E=!  
curl -v "http://example.org/${E}287s87asdjh2/somepath/someresource"

ここでのシンプルさは、(1)シェルとコマンド間で移植可能であることです(2)エスケープ構文とASCIIコードを知っている必要はありません。


3

Bash 4.3以降、二重引用符を使用して履歴拡張文字を引用できるようになりました。

$ bash --version
GNU bash, version 4.3...
[...]
$ echo "Hello World!"
Hello World!

外このdoesntの仕事はecho、エコーは異なり、独自にこれを処理するようだ
phil294

@Blauhirnこれはエコーとは関係がなく、クォートと実行中のbashのバージョンとは関係がありません。
-Flimm

2
この答えは間違っているため、削除する必要があります。お使いのbashバージョンは、バンが展開されていないこととは関係ありません。これは、例で!は「行末」が後に続き、シェルが展開しようとするのを防ぐためです。試してみてください 。返事echo "!Hello World"が表示されます。詳細については、マニュアルを参照してくださいbashbash: !Hello: event not found
don_crissti

0

Windowsでgit bashを使用している人には、@ DanielPittmanから受け入れられた答えが機能します。ただし、円記号(\)をスラッシュ(/)に置き換える必要があります。

たとえば、Unixでは、次のようになります。

curl https://abc.com/services -H 'Authorization: Bearer 111A80BBZZCnS\!ZR412543s'

Windowsの場合、このようなものになります(認証ヘッダー部分のスラッシュに注目してください)

curl https://abc.com/services -H 'Authorization: Bearer 111A80BBZZCnS/!ZR412543s'


これはあまり意味がありません。引数は一重引用符で囲まれているため、関係するスラッシュに関係なく、エクスクラムは履歴を拡張しません。
ワイルドカード

そうだね。ダニエルの回答(バックスラッシュを使用)を使用していたときにエラーが表示されるため、この回答のみを掲載しました。
-SamuelDev
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.