感嘆符「!」が時々バッシュを台無しにするのはなぜですか?

感嘆符「!」が時々バッシュを台無しにするのはなぜですか?

!コマンドライン履歴の文脈では、これがコマンドラインに特別な意味があることを知っていますが、それ以外にも感嘆符は時々スクリプト実行時に解析エラーを引き起こす可能性があります。
aと関係があるようですが、eventaが何なのかわかりません。イベントそれが何であるか、何をするか。それにもかかわらず、同じコマンドが状況によって異なる動作をする可能性があります。
以下の最後の例ではエラーが発生しますが、同じコードがコマンド置換の外部で動作する場合、なぜ動作しますか? ..GNU bash 4.1.5の使用

# This works, with or without a space between ! and p
  { echo -e "foo\nbar" | sed -nre '/foo/! p'
    echo -e "foo\nbar" | sed -nre '/foo/!p'; }
# bar
# bar

# This works, works when there is a space between ! and p
  var="$(echo -e "foo\nbar" | sed -nre '/foo/! p')"; echo "$var"
# bar

# This causes an ERROR, with NO space between ! and p
  var="$(echo -e "foo\nbar" | sed -nre '/foo/!p')"; echo "$var"
# bash: !p': event not found

答え1

この!役割はbashの履歴置換を呼び出します(対話型シェルでデフォルトで有効になっています)。失敗した例のように、文字列が後に続く場合は、その文字列で始まる最後の履歴イベントに拡張しようとします。この$var文字列の値を拡張すると、!echo履歴の最後のechoコマンドまで拡張されます。

スペースは、これらの拡張の破壊的な機能です。まず、これが変数をどのように処理するかを見てください。

# var="like"
# echo "$var"
like
# echo "$"
$
# echo "Do you $var frogs?"
Do you like frogs?       <- as expected, variable name broken at space
# echo "Do you $varfrogs?"
Do you?                  <- $varfrogs not defined, replaced with blank
# echo "Do you $ var frogs?"
Do you $ var frogs?      <- $ not a valid variable name, ignored

歴史拡張でも同じことが起こります。感嘆符(!)は履歴置換シーケンスを開始しますが、後に文字列が続く場合にのみ適用されます。置換シーケンスの一部ではない文字通りの爆発を作成するには、スペースを追加します。

一重引用符を使用すると、これらの変数の置換と履歴の拡張を防ぐことができます。最初の例では一重引用符を使用しているため、正しく機能します。最後の例は二重引用符で囲まれているため、bashは他の操作を実行する前に拡張シーケンスを検索します。最初のものがトリップしない唯一の理由は、上記のように空白が壊れる文字であるためです。

答え2

すでにケイロップ!bashの履歴置換を呼び出すために使用されます。

このような機能が必要ないと思われる場合は、次の行を挿入して機能を無効にできます~/.bashrc

set +H

上矢印と - 増分リバース検索で履歴を復元できるため、必要ありませんCtrlrbashのマニュアルページのセクションを参照してください。記録を操作するコマンド詳細なショートカットのリストについて学びます。

答え3

最初の例:

{ echo -e "foo\nbar" | sed -nre '/foo/! p'
    echo -e "foo\nbar" | sed -nre '/foo/!p'; }

に減らすことができます

echo '! p' 
echo '!p'

一重引用符内で、すべての文字はリテラル値を保持します。その結果、!その特別な意味を喪失し、歴史の展開も進まなかった。

2番目と3番目の例:

var="$(echo -e "foo\nbar" | sed -nre '/foo/! p')"; echo "$var"

var="$(echo -e "foo\nbar" | sed -nre '/foo/!p')"; echo "$var"

に減らすことができます

echo "'! p'"

echo "'!p'"

'! p''!p'本質的に二重引用符で囲まれた文字列の一部です。

二重引用符内のすべての文字はリテラル値を保持します。とは別に $、、、そして。`\!

'! p'これは、との一重引用符が'!p'特別な意味を失ったが(例:エスケープできない!)、!まだ特別な意味を維持して歴史的拡張を実行することを意味します。

ただし、!後に空白文字が続くと、履歴拡張は行われません。

引用元man bash:

引用する

[...]

文字を一重引用符で囲むと、引用符内の各文字のリテラル値が保持されます。 [...]

文字を二重引用符で囲むと、履歴拡張が有効になっている場合は、$、`、\、!を除く引用符内のすべての文字のリテラル値が保持されます。 [...]有効にすると、!が二重引用符で囲まれ、バックスラッシュでエスケープされない限り、履歴拡張が実行されます。 !前のバックスラッシュは削除されません。

歴史的拡張

[...]

歴史拡張キャラクターの登場を通じて歴史拡張が紹介されること、つまり!基本的に。バックスラッシュ(\)と一重引用符のみが履歴拡張文字を引用できます。

後ろに履歴拡張文字が続くと、引用符がなくてもレコード拡張を抑制するいくつかの文字(スペース、タブ、改行、キャリッジリターン、=)があります。 (extglobシェルオプションが有効な場合、拡張も抑制されます。

関連情報