強打をエコーする方法!


59

echoエディターで開くのではなく、コンテンツをファイルに入れてスクリプトを作成しようとしました

echo -e "#!/bin/bash \n /usr/bin/command args"  > .scripts/command

出力

bash:!/ bin / bash:イベントが見つかりません

私はこの奇妙な行動を強打に隔離しました。

$ echo !
!  

$ echo "!"
bash: !: event not found

$ echo \#!
#!

$ echo \#!/bin/bash
bash: !/bin/bash: event not found
  • なぜbangはこれを引き起こしていますか?
  • bashが参照するこれらの「イベント」とは何ですか?
  • この問題を乗り越えて画面またはファイルに「#!/ bin / bash」を印刷するにはどうすればよいですか?

回答:


59

一重引用符を使用してみてください。

echo -e '#!/bin/bash \n /usr/bin/command args'  > .scripts/command

echo '#!'

echo '#!/bin/bash'

この問題は、bashが!/ bin / bashの履歴を検索しているために発生しています。単一引用符を使用すると、この動作が回避されます。


3
また、文字列の補間を無効にします:(
Hubro

それがポイントです!二重引用符を使用して同じechoコマンドに他の引数を追加し、それらの部分で補間を取得できます。
リッチム

3
また、bashでCスタイルの文字列を使用することもできます$''。たとえば、echo $'1!\n2!\n3!\n'各番号に続いてバングと改行を出力します。
spelufo

1
複数行の文字列を入力するとき、一重引用符で強打することは役に立ちませんでした:!
bash—

1
@kbolinoそのとおりです。したがって、完全な例の文字列は次のようになります。echo '0123'"$var"'456'単一引用符の2つのペアと二重引用符の1つのペアに注意してください。
-phyatt

16

以下のようRichmが言った、bashがやろうとしている歴史の試合を。それを回避する別の方法は、単にbangをエスケープすることです\

$ echo \#\!/bin/bash
#!/bin/bash

二重引用符の内側には注意が必要ですが、これ\は削除されません:

$ echo "\!"
\!

8
bashでは、おそらくPOSIX準拠のため"\!"\!、ではなくに展開され!ます。
ミケル

@Mikelため息。zshとbashの非常に多くの違い
Michael Mrozek

これは、複数行の文字列を入力する場合でも機能します。1. bangを入力するときに文字列の外にいること、および2.このメソッド(バックスラッシュ)を使用することは、ほとんどのシナリオで驚くほど少なく機能するようです。
ビンキ

1
bashはスクリプトの実行時に履歴検索を行わないため、そこで特別なことをする必要がないことに注意してください。
ビンキ

!魔法の振る舞いをせずに二重引用符内で単純にエスケープする構文はありませんか?これはナッツです...
ラッシー

13

!履歴置換を開始します(「イベント」はコマンド履歴の行です)。たとえば、を!ls含む最後のコマンドラインに展開し、を含む最後のコマンドラインに展開します。特定の単語(たとえば、前のコマンドの最初の単語を指す)などを抽出することもできます。詳細については、マニュアルを参照してください。ls!?foofoo!!:1

この機能は、コマンドラインエディションが原始的な時代に以前のコマンドをすばやく呼び出すために考案されました。最新のシェル(少なくともbashとzsh)とコピーアンドペーストを使用すると、履歴の展開は以前ほど便利ではありません。それでも役に立ちますが、それなしでも大丈夫です。

histchars変数を設定することで、どの文字が履歴置換をトリガーするかを変更できます。履歴の置換をめったに使用しない場合は、たとえばの代わりに履歴の展開histchars='¡^'¡トリガーするように設定できます!。で機能を完全にオフにすることもできますset +o histexpand


3

特定のコマンドラインで履歴展開を無効にするにspaceは、次の3番目の文字として使用できます$histchars

histchars='!^ '

その後、先頭にスペースを入れてコマンドを入力すると、履歴の展開は実行されません。

bash-4.3$ echo "#!/bin/bash"
bash: !/bin/bash: event not found
bash-4.3$  echo "#!/bin/bash"
#!/bin/bash

ただし、履歴にコマンドラインを記録しないように指示する方法として$HISTCONTROLが含まれる場合、先頭のスペースも使用されることに注意してください。ignorespacebash

両方の機能を独立して使用する場合は、の3番目の文字として別​​の文字を選択する必要があります$histchars。コマンドの解釈方法に影響しないものが必要です。いくつかのオプション:

  • バックスラッシュ(\)を使用:\echo foo動作しますが、エイリアスまたはキーワードを無効にする副作用があります。
  • TAB:最初の位置に挿入するには、押す必要がCtrl+VTabあります。
  • あなたは2つのキーを入力して気にしないならば、あなたは通常、最初の位置に表示されていない任意の文字を選ぶことができます(%@?、自分自身を選択)し、そのための空のエイリアスを行います。

    histchars='!^%'
    alias %=

    次に、その文字、スペース、およびコマンドを入力します。

    bash-4.3$ % echo !!
    !!

(あなたは、履歴置換がが無効になっているコマンドを記録しないことができません。また、デフォルトの3番目の文字があることに注意$histcharsされた#ので、履歴展開は、あなたがそれを変更した場合。コメントの中で行われ、あなたがでコメントを入力されていませんプロンプト、!シーケンスがそこで展開される可能性があるという事実に注意する必要があります)。


2

提案されたソリューションは、たとえば次の例では機能しません。

$ bash -c "echo 'hello World!'"
-bash: !'": event not found
$

この場合、バングは8進数のASCIIコードを使用して印刷できます。

$ bash -c "echo -e 'hello World\0041'"
hello World!
$

1
最初のコマンドでは、!は二重引用符で囲まれています。他の回答が示すように、これは履歴拡張の意味を保持しています。bash -c 'echo '\''hello World!'\'またはを使用できます bash -c "echo 'hello World"\!"'"
ジル 'SO-悪であるのをやめる'

-iパラメーターがないと、環境が変更される可能性があります(〜/ .profileが実行されないなど)。ただし、-iを実行すると、.profileからの出力が、実際に実行するコマンドの出力にキャプチャされます。
ブレント

-1

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

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

4
いいえ、!その後に続くの"は特別なことではありません。echo "foo!"、OKですecho "foo!bar"されていません
ステファンChazelas
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.