history expansion(履歴展開)入門 - !! や !$ でコマンド再利用を高速化
この記事でわかること
- history expansion(履歴展開)が「過去コマンドを呼び出す記法」だと分かる
!!/!$でコマンドや引数を一瞬で再利用できる!string/!:n/:h:t:rで履歴を細かく取り出せる^old^new^のクイック置換と、histverifyで安全に使う方法が分かる
sudo !! とか cd !$ ってよく見るんですけど、あの ! マークって何なんですか?! から始まる記法を打つと、過去のコマンドや引数を呼び出して再利用できるんだ。タイプ量がぐっと減るよ。1. history expansion とは何か?
結論: history expansion は
!で始まる記法を、過去のコマンドや引数に展開する bash の機能。タイプ量を減らし再入力ミスを防ぐ。
!! のような記法を「実際のコマンド文字列」に置き換えることだよ。例えば !! は直前のコマンド全体に置き換わるんだ。history expansion は大きく 3 つの部品でできています。
| 部品 | 役割 | 例 |
|---|---|---|
| イベント指定子(event) | どのコマンドを呼ぶか | !! / !42 |
| ワード指定子(word) | そのコマンドのどの単語を取り出すか | !$ / !^ |
| 修飾子(modifier) | 取り出した単語をどう加工するか | :h / :t |
history expansion は主に bash の対話シェル で有効です。スクリプト内ではデフォルト無効なので、「便利だけどスクリプトには書かない」と覚えておきましょう。
2. イベント指定子:どのコマンドを呼ぶか
結論:
!!は直前、!nは履歴番号 n、!stringは string で始まる直近、!?string?は string を含む直近のコマンドを指す。
!! =直前のコマンド。sudo を付け忘れたときの定番テクだよ。!! — 直前のコマンド全体
$ apt update E: Could not open lock file /var/lib/dpkg/lock-frontend ... $ sudo !! sudo apt update
!n / !-n — 履歴番号で指定
$ history | grep tar 765 tar -czf backup.tar.gz /etc $ !765 tar -czf backup.tar.gz /etc
!n は履歴番号 n のコマンド、!-n は「n 個前」のコマンドを指します(!-1 は !! と同じ)。
!string — string で始まる直近のコマンド
$ !ssh ssh user@server
!string は「string で始まる、もっとも最近のコマンド」を再実行します。
!?string? — string を含む直近のコマンド
$ !?server? ssh user@server
!?string? は「string をどこかに含む直近のコマンド」を探します。先頭以外にもマッチする点が !string との違いです。
!string は確認なしで即実行されます。意図しない古いコマンドが呼ばれることもあるので、不安なときは次章の :p(表示のみ)で中身を確かめましょう。
3. ワード指定子:引数だけを取り出す
結論:
!$は最後の引数、!^は最初の引数、!*は全引数、!:nは n 番目の単語を取り出す。長いパスの再入力を防げる。
!$ は「直前コマンドの最後の引数」。長いパスをもう一度打たずに済むんだ。!$ — 最後の引数
$ mkdir -p /var/log/myapp $ cd !$ cd /var/log/myapp
!^ と !* — 最初の引数 / 全引数
$ cp config.yml config.yml.bak $ vim !^ vim config.yml
!^ は最初の引数、!* はコマンド名を除いたすべての引数を表します。
!:n — n 番目の単語
単語には 0 から番号が振られ、0 がコマンド名そのものです。
$ cp src.txt dst.txt $ echo !:1 echo src.txt $ echo !:2 echo dst.txt
| ワード指定子 | 意味 |
|---|---|
!^ |
最初の引数(!:1 と同じ) |
!$ |
最後の引数 |
!* |
すべての引数(!:1-$) |
!:0 |
コマンド名そのもの |
!:n |
n 番目の単語 |
!:n-m |
n から m 番目までの単語 |
!$ などは「直前コマンド」が暗黙の対象です。別のコマンドの引数がほしいときは !ssh:$ のようにイベント指定子と組み合わせます。
4. 修飾子:取り出した単語を加工する
結論:
:hはディレクトリ部分、:tはファイル名、:rは拡張子を除いた部分、:eは拡張子を取り出す。:pは実行せず表示のみ。
:h や :t を付けると、パスを部品に分解できるよ。$ ls /var/log/syslog $ echo !$:h echo /var/log $ echo !$:t echo syslog
| 修飾子 | 動作 | 例(/var/log/app.log) |
|---|---|---|
:h |
head:末尾の要素を除く(ディレクトリ) | /var/log |
:t |
tail:末尾の要素のみ(ファイル名) | app.log |
:r |
root:拡張子を除く | /var/log/app |
:e |
extension:拡張子のみ | log |
:p |
print:展開結果を表示するだけ(非実行) | — |
:p で「展開結果だけ」を確認する
$ !ssh:p ssh user@server
:p を付けると、history expansion の結果を実行せずに表示します。展開結果は履歴に追加されるので、確認してから !! で実行できます。
rm や sudo を含むコマンドを !string で呼ぶときは、まず :p で中身を確認する習慣をつけると事故を防げます。
5. クイック置換:^old^new^
結論:
^old^new^は直前コマンドの最初の old を new に置換して再実行する短縮記法。タイプミスの修正に最適。
^old^new^ を使えば、直前コマンドの old を new に置き換えて再実行できるよ。タイプミス修正の鉄板テクだね。$ cat /etc/hosst cat: /etc/hosst: No such file or directory $ ^hosst^hosts^ cat /etc/hosts
^old^new^ は !!:s/old/new/ の短縮形で、最初に一致した 1 箇所だけを置換します。すべて置換したいときは !!:gs/old/new/ を使います。
末尾の ^ は省略できます(^hosst^hosts でも動作)。ただし置換後にさらに文字を足したい場合は末尾 ^ まで明示しましょう。
6. histverify で安全に使う
結論:
shopt -s histverifyを設定すると、history expansion が即実行されず編集行に展開される。確認してから Enter できる。
!! って確認なしで実行されるのが、ちょっと怖いです。histverify で解消できるよ。設定しておくと、!! を展開した結果がいったん入力行に表示されて、Enter を押すまで実行されないんだ。~/.bashrc に次の 1 行を追記します。
shopt -s histverify
設定後は source ~/.bashrc で反映します。これ以降、!! や !string を打つと、展開後のコマンドが入力行に現れ、内容を確認・編集してから Enter で実行できます。
histverify は破壊的コマンドの誤実行を防ぐ安全装置です。history expansion を日常的に使うなら設定しておくと安心です。
7. まとめ
結論: イベント指定子・ワード指定子・修飾子の 3 部品を組み合わせれば、過去コマンドを自在に再利用できる。
history expansion の基本記法を一覧で振り返ります。
| 記法 | 意味 |
|---|---|
!! |
直前のコマンド全体 |
!n / !-n |
履歴番号 n / n 個前のコマンド |
!string |
string で始まる直近のコマンド |
!$ / !^ |
最後の引数 / 最初の引数 |
!* |
すべての引数 |
!$:h :t |
パスのディレクトリ / ファイル名 |
^old^new^ |
直前コマンドを置換して再実行 |
!!:p |
展開結果を表示のみ(非実行) |
! ひとつでこんなに呼び出し方があるんですね。まずは !! と !$ から使ってみます!!string や ^old^new^ を足していけば、コマンド入力がどんどん速くなるよ。