0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

bashで!を含む文字列をそのまま実行しようとすると、以下のような見慣れないエラーに遭遇することがあります。

-bash: !foo: event not found

パスワードに!が含まれている、メッセージ文に「〜してください!」と感嘆符を使っている、といった何気ない場面で突然出てくるため、原因が分かりにくいエラーの1つです。本記事では、この現象の正体である履歴展開(history expansion) の仕組みと対処法を整理します。

原因:!はbashにとって特別な文字

bashの対話的シェルには、過去に実行したコマンドを!を使って呼び出す履歴展開という機能が備わっています。デフォルトでは!が履歴展開の開始文字(history expansion character)として扱われます。

!!      # 直前に実行したコマンドを再実行する
!123    # 履歴の123番目のコマンドを再実行する
!ls     # 履歴の中で直近に実行した「ls」で始まるコマンドを再実行する

この機能があるために、!の直後に何らかの文字列が続いていると、bashはそれを「履歴からコマンドを探せ」という指示だと解釈しようとします。該当する履歴が見つからない場合に返ってくるのが、冒頭のevent not foundエラーです。

見落としがちな注意点:シングルクォートでも防げない

多くのシェル機能は、シングルクォート('...')で囲むことで文字列をそのまま(展開なし)扱えます。しかし、履歴展開だけは対話的シェルにおいて例外で、シングルクォートで囲んでいても!の展開が発生してしまうことがあります。

echo 'Hello!World'
# → シングルクォートの中なのに、bashのバージョン・設定によっては
#   history expansion が発生してしまうことがある

これは、履歴展開がシェルの構文解析(クォートの処理)よりも前の、字句解析の早い段階で行われるために起きる、bash特有の癖です。ダブルクォートで囲んだ場合も同様に防げません。

対処法

1. バックスラッシュでエスケープする

!の直前にバックスラッシュ(\)を置くことで、履歴展開の対象から明示的に除外できます。

echo Hello\!World

2. 履歴展開そのものを無効化する(set +H)

そのシェルセッション全体で履歴展開を使う予定がないなら、set +H(またはset +o histexpand)で機能自体を無効化してしまう方法もあります。

set +H
echo 'Hello!World'   # もう event not found にはならない

パスワードなど!を含む値をスクリプトの引数として渡す必要がある場面(例: DBのパスワードに記号として!が含まれる場合)では、この方法が特に有効です。元に戻したい場合はset -Hで再度有効化できます。

3. 非対話的シェル(スクリプト)では基本的に問題にならない

履歴展開は「対話的な」シェルセッションでのみデフォルト有効な機能です。.shスクリプトファイルをbash script.shのように実行する場合は、通常この設定はデフォルトで無効になっているため、スクリプト内に!を含む文字列があっても同じ問題は起きません。問題になるのは、主にターミナルで直接コマンドを打つ・貼り付けるような対話的操作の場面です。

まとめ

項目 内容
原因 bashの履歴展開機能が!を「履歴からコマンドを呼び出す指示」と解釈してしまう
発生条件 対話的シェルで!の直後に、履歴に一致しない文字列が続いている場合
よくある誤解 シングルクォート・ダブルクォートで囲んでも、履歴展開自体は防げない
対処法 \!のようにバックスラッシュでエスケープする、またはset +Hで履歴展開そのものを無効化する

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?