本文へ移動
vast-cowのブログ
前のページへ戻る

Bashの履歴ファイルを安全に手動編集する方法

この記事を編集

Bash のコマンド履歴は通常 $HISTFILE(多くの環境では ~/.bash_history)に保存されています。

不要な履歴をまとめて削除したい場合など、このファイルを直接エディタで編集したくなることがあります。

ただし、複数の Bash セッションを同時に開いている環境では注意が必要です。単純に $HISTFILE を編集すると、別の Bash セッションが持っている履歴によって、後から編集内容が上書きされる可能性があります。

この記事では、複数の Bash セッションを開いたまま、履歴ファイルをできるだけ安全に編集する手順を整理します。

Bashの履歴には「メモリ」と「ファイル」がある

まず押さえておきたいのは、Bash の履歴が単純に $HISTFILE だけで管理されているわけではないことです。

実行中の各 Bash プロセスは、それぞれ独立して履歴をメモリ上に保持しています。

例えば3つのターミナルで Bash を起動している場合、

Bash A ── メモリ上の履歴
Bash B ── メモリ上の履歴
Bash C ── メモリ上の履歴
              ↓
         $HISTFILE

という状態になります。

そのため $HISTFILE を直接編集する場合は、ファイルだけではなく、各 Bash プロセスが持っている履歴も考慮する必要があります。

まず全セッションの履歴を書き出して記録を停止する

最初に、現在開いているすべての Bash セッションで次を実行します。

history -a
set +o history

history -a は、その Bash セッションでまだ履歴ファイルに保存されていない履歴を $HISTFILE に追記します。

つまり、全セッションで実行することで、

Bash A ─┐
Bash B ─┼─ history -a → $HISTFILE
Bash C ─┘

という形で、それぞれの未保存履歴をファイルに集められます。

続く

set +o history

は、その Bash セッションでの履歴記録を一時的に無効化します。

これも全セッションで実行するのがポイントです。

1つのセッションから履歴ファイルを編集する

すべての Bash セッションで

history -a
set +o history

を実行したら、1つのセッションから $HISTFILE を編集します。

例えば Emacs なら、

emacsclient -nw "$HISTFILE"

もちろんエディタは何でも構いません。

vim "$HISTFILE"

や

"${EDITOR:-vi}" "$HISTFILE"

でも構いません。

ここで不要な履歴を削除するなど、必要な編集を行って保存します。

編集後は全セッションの履歴を再読み込みする

ファイルを編集しただけでは、既に起動している Bash のメモリ上には編集前の履歴が残っています。

そこで、再びすべての Bash セッションで次を実行します。

history -c
history -r "$HISTFILE"
set -o history

それぞれの意味は次のとおりです。

history -c

現在の Bash がメモリ上に保持している履歴をクリアします。

続いて、

history -r "$HISTFILE"

編集済みの $HISTFILE を読み込みます。

最後に、

set -o history

で履歴記録を再び有効化します。

これによって各 Bash セッションは、

              編集済み $HISTFILE
                    │
          history -r│
          ┌─────────┼─────────┐
          ↓         ↓         ↓
       Bash A    Bash B    Bash C

という状態になり、全セッションが編集済みの履歴を基準に動作するようになります。

手順をまとめると

1. 全 Bash セッションで実行

history -a
set +o history

2. 1つの Bash セッションで履歴を編集

"${EDITOR:-vi}" "$HISTFILE"

3. 全 Bash セッションで実行

history -c
history -r "$HISTFILE"
set -o history

これが基本的な流れです。

なぜ history -w ではなく history -a なのか

一見すると、編集前に

history -w "$HISTFILE"

を実行した方が確実そうに見えます。

しかし、複数の Bash セッションが存在する場合には注意が必要です。

history -w は、現在の Bash がメモリ上に持っている履歴を使って $HISTFILE を書き直します。

例えば、

Bash A の履歴: A1 A2 A3
Bash B の履歴: B1 B2 B3

という状態で、Bash B の履歴が $HISTFILE に反映されていても、Bash A がそれを読み込んでいない可能性があります。

その状態で Bash A から

history -w "$HISTFILE"

すると、Bash A が知っている履歴を基準にファイルが書き直され、他セッション由来の履歴を失う可能性があります。

これに対して、

history -a

は未保存の履歴を追記します。

そのため複数セッションから履歴を集める用途では、

history -a

を使う方が適しています。

それでも「完全」ではない

ここまで行えばかなり安全ですが、厳密には完全な方法ではありません。

Bash の履歴ファイルは、複数の Bash プロセスから同時に更新するためのトランザクショナルなデータベースではありません。

つまり、

history -a

を各セッションで実行している最中に別セッションで新しいコマンドが実行されたり、履歴ファイルへの書き込みが競合したりすれば、タイミング依存の問題が発生する余地があります。

また、

set +o history

を実行した後のコマンドは、そのセッションの通常の履歴には記録されません。

したがって、履歴編集作業中に通常の作業を続けるのは避けた方がよいでしょう。

最も確実なのは他の Bash を終了すること

確実性を優先するなら、もっと単純な方法があります。

履歴を編集する Bash 以外のセッションを終了してしまうことです。

複数プロセスが同じ $HISTFILE を扱っていること自体が競合の原因なので、

Bash A
Bash B
Bash C

を

Bash A

だけにしてから履歴を編集すれば、考慮すべき問題は大幅に減ります。

つまり、

という使い分けになります。

結論

複数の Bash セッションを開いたまま $HISTFILE を編集するなら、次の手順が実用的です。

編集前に全セッションで

history -a
set +o history

1セッションで

"${EDITOR:-vi}" "$HISTFILE"

編集後に全セッションで

history -c
history -r "$HISTFILE"
set -o history

重要なのは、$HISTFILE だけを編集して終わりにしないことです。

各 Bash プロセスはそれぞれ独立した履歴をメモリ上に保持しています。そのため、編集前には各セッションの未保存履歴を $HISTFILE に集約し、編集後には全セッションを編集済み $HISTFILE の内容に同期する、という考え方になります。

ただし Bash の履歴管理には複数プロセス間の強力な排他制御があるわけではありません。履歴を確実に編集する必要がある場合は、他の Bash セッションを終了してから作業する方法が最もシンプルです。


この記事を編集
この記事を共有:

コメント


前の記事
Git Rebaseで過去のコミットをSquashする方法
次の記事
GitHubのMarkdownで使える「Alerts」記法とは?