LoginSignup
17
14

More than 3 years have passed since last update.

fish shellでhistoryを使いこなす

Last updated at Posted at 2019-08-28

はじめに

もしかすると誰もが通る道なのかもしれないが、historyコマンドを使いこなせたら作業早くなるかもなぁ、と思って調べた内容。(コマンド全て覚えるのはめんどうというのもある
myPCがfishだったばかりに、bashとは違う情報がいろいろ出てきて面白かったので、まとめてみました。
これを読めば、

  • fishhistoryの使い方について
  • fishhistoryを使うときに調べるであろうことについて

知ることができると思います。

随所に、悪い癖で自分の思ったことをつらつらつらと書いた箇所があるので私がなぜにfishだったのか?「ユーザーフレンドリ」なものに悪いものはいない?など、時間ない人は読み飛ばしちゃってください。

私がなぜにfishだったのか

もちろん、myPCにfishを入れた犯人は私です。
一番の理由は、新入社員だった頃に、若手の先輩に激推しされたことでした。
聞くところによると、「ユーザーフレンドリ」なシェルらしいということで、「ユーザーフレンドリ」なものに悪いものはないだろうと入れました。
今回、fishhistoryを調べたことで、fishの概念のようなものも、ちょっとは知ることができた気がするので、そこいらへんも少し書きたいと思います。

bashhistoryとの違い

書式とオプション(サブコマンド)の違い

一番大きな違いは、「サブコマンドという概念」と、「コマンドのオプションの内容」かと思います。
まずは、比較のためbashhistoryの書式と主なオプションについておさらいしてみましょう。

bashの場合

history [オプション]
history オプション [ファイル]
オプション 意味
整数値 履歴の末尾から指定した行数分を表示
-c 履歴一覧から全ての項目を削除
-d 番号 指定した番号の履歴項目を削除
-a 履歴ファイルに現在のセッションの履歴を追加
-n 履歴ファイルから読み込まれていない行を全読み込み
-r ファイル 履歴ファイルを読み込み、内容を履歴一覧に追加
-w ファイル 現在の履歴を履歴ファイルに上書き

次はfishhistoryについてですが、サブコマンドの影響で、bashhistoryよりも書式が多彩になっています。

fishの場合

# 検索文字列にマッチする履歴を出力
history search [オプション] [ 'search string'... ]

# 指定された文字列に該当する履歴のエントリを削除
history delete [オプション] 'search string'...

# 他のfishセッションからの履歴の更新をマージ
history merge

# すべての更新を履歴ファイルに書き込み(自動的に保存されるのでほぼ使用しない)
history save

# 履歴ファイルをクリア
history clear

# 全履歴を出力(結果的には history = history search となる)
history

サブコマンドとはhistoryに続く、
searchdeletemergesaveclear の部分です。
それぞれ上記で示した用途で利用されます。

オプション 意味 対応しているサブコマンド
-C / --case-sensitive 大文字小文字を区別 search/delete
-c / --contains 指定された文字列に部分一致 search/delete 
-e / --exact 指定された文字列に完全一致 search/delete 
-p / --prefix 指定された文字列に前方一致 search/delete 
-t / --show-time history項目の日時表示 search/delete 
-z / --null 改行の代わりにヌル文字を表示の区切りとして使用 search
-<number> / -n <number> <number>にマッチした数だけhistory項目を表示 search
-R / --reverse 新しい順から古い順に変更 search

補足
-<number> / -n <number>は他に--max=<number>という書き方もあります。
history = history searchのため、searchで利用できるオプションはhistoryでも利用できます。

何もかも違うと言って過言じゃないですが、
何となく違いを捉えるなら「fishの方がhistoryの検索に長(た)けている」という理解で大丈夫だと思います。

少し自分なりの書き方をしている部分もありますので、正確な情報は以下を参照してください。
公式サイトのhistoryのページ
全訳!fishシェル普及計画のhistoryのページ

使い方の違い

サブコマンドsearchdeleteの使い方について一例を示してみます。
例) whichコマンドでパスの確認を行った履歴を日時とともに表示する。

history search -t which

# 出力
# 金  8/ 9 20:41:17 2019
which pyenv
# 金  8/ 9 20:40:39 2019
which node
# 金  8/ 9 20:40:11 2019
history which
# 水  5/29 00:17:15 2019
which fish
# 水  5/15 21:16:26 2019
which python
# 水  5/15 20:38:13 2019
which bash

なんだったら、(さっきからしつこく書いてますが)history = history searchだと使っていたら分かるので、searchも書かなくていいです。historyはデフォルトでgrepの機能もくっついてるようなものだと思ってもらったらいいと思います。

例)whichコマンドの履歴の一部を削除する

history delete -p which

#出力
[1] which pyenv
[2] which node
[3] which fish
[4] which python
[5] which bash

Enter nothing to cancel the delete, or
Enter one or more of the entry IDs separated by a space, or
Enter "all" to delete all the matching entries.

Delete which entries? >

deleteのサブコマンドを使うと、上記のような出力結果が得られます。
検索した全ての履歴を削除したい場合はallをタイプしてEnterしたらいいし、which nodewhich fishを削除したいなら、2 3とタイプしてEnterしたらOKです。

deleteを使って複数の検索結果を得たい場合は、基本的に部分一致の-c / --containsか前方一致の-p / --prefixのオプションをつけます。

(余談)
公式ドキュメントを読む限りは、deleteのデフォルトのオプションは-e / --exactですが、挙動および公開されているソースを確認すると-c / --containsになっています。ドキュメントとソースどちらが間違っているか、今のところはっきりとしていません。

表示の挙動の違い

bashと比べて以下のような表示の違いがあります。

  • historyの項目が、新しい順で表示される。
  • (コマンドラインの)1画面ずつhistoryの項目が表示される。(デフォルトでhistory | moreを実行したようなイメージ)

確かに、古い順で全部表示されるより、新しい順で画面に出力できる範囲で表示された方が便利ですよね。(流石「ユーザフレンドリ」)

bashでよくやる設定について

さてさて、fishbashhistoryの違いはよく分かった、お腹いっぱいとなった私。
今度はfishhistoryを使いこなしたい(便利に使いたい)!と思い立つことになります。
そして私は気づくのです。「bashだとhistoryを便利にするために、いろいろと設定するよな?」ということに。
つまり、bashで設定することをfishだったらどうやって設定するんだろうと思ったわけです。

以下、いろいろと調べましたが、設定できなかったものもありますのでご注意ください。

コマンド履歴の保存件数を増やす

デフォルトの500個は少なすぎる!ってことでbashだと一番最初に設定するのではないでしょうか。お馴染みHISTSIZEに値を設定する作業。

fishはどうやって設定するのか?
答えは、なんと設定できないという落ちになります。
実は、fishにHISTSIZEなんてものはない
デフォルトで256k個保存できるから大丈夫っしょ?って感じ↓
https://github.com/fish-shell/fish-shell/issues/2674

そうなのか・・・。まあ256k個もあるならいいか・・・

historyに日時を付与する

bashのノリでいくと、HISTTIMEFORMATに日時の出力形式を設定する、という話になるわけだが、HISTTIMEFORMATに類するものはfishには(これまた)存在しない。
オプションの-t / --show-timeを使いましょう、ということらしい。

オプションつけるのも面倒くさい!!という人にはaliasで設定するという方法があるかと思います。

historyに日時を付与するaliasの設定方法(あくまで一例)

 alias thistory "history --show-time='%Y-%m-%d %H:%M:%S '"
 funcsave thistory

表示形式の変更する場合は、基本的には-tの短縮形ではなく--show-timeを使います。
上記で定義したthistoryの実行結果は以下の通りです。

thistory -p which

# 出力
2019-08-09 20:41:17 which pyenv
2019-08-09 20:40:39 which node
2019-05-29 00:17:15 which fish
2019-05-15 21:16:26 which python
2019-05-15 20:38:13 which bash

ちなみにデフォルの表示形式だと使い方の違いで示したように、改行が入ります。
(上記の表示形式の方が慣れている人が多いかもしれません)

〜横道〜 aliasの話

historyに日時を付与するで示した通り、fishにおけるalias設定ファイルに記述するのではなく、コマンドラインで設定(alias)して保存(funcsave)することができます。
「ユーザフレンドリ」としてはかなりの美点なのではないでしょうか。

が、しかし、、元々存在する関数と同名の別名は定義できません。とのこと。

やってみると、できてしまうのですが(苦笑)
挙動がおかしくて、戻し方を知らないと混乱するのでやらないのが無難です。

ちなみにfishaliasは中身的にはfunctionコマンドになります。

参考
aliasのドキュメント(公式を元にした日本語翻訳版)
functionのドキュメント(公式を元にした日本語翻訳版)

また、abbrコマンドというものもあります。(どちらかというと、こちらの方が他のシェルのalias、別名定義に近いようです)↓

abbrのドキュメント(公式を元にした日本語翻訳版)

短縮形を管理するコマンドではありますが、abbrだと同名での定義もできます。
abbralias(function)の違いも、色々と試してみましたが、奥が深そうでした・・・。今回は内容書き出すと趣旨とどんどんずれていくので割愛します。

特定のコマンドの履歴を保存しない

lshistoryコマンド自体など、別段historyに残しておかなくていいコマンドを保存しておきたくない!!ってときの話。

まず前提ですがfishには以下のような特徴があります。

  • そもそも重複するコマンドの履歴は保存されない。
  • 先頭に空白を記述すると、履歴に保存されない

上記がデフォルトの挙動です。
history -e llとかしても結果は一つしか出てこないし、llコマンドを実行する度に記録される時間は更新される、、といった具合です。

bashでいう、
HISTCONTROL=ignorebothのような状態です。

さてさて、「特定のコマンドの履歴を保存しない」設定ですが、当然のようにfishにはHISTCONTROLHISTIGNOREに類するものはありません・・・。

先人の知恵としては以下のようなものがあります。
(1) https://github.com/fish-shell/fish-shell/issues/2788
issueの回答として以下のようなfunctionが記載されています。

function ignorehistory --on-event fish_prompt # or maybe fish_preexec, see function --help
    history --delete fg bg
end

簡単な説明ですが、
上記のfunctionの意味は「fish_promptが実施される度にhistoryの中のfgbgのコマンドの履歴を削除する」ってな具合。
fish_promptについては(超入門)fishでプロンプトを変えたい人へのチュートリアルを参照ください。

fish_promptが実施される度」というのは、つまりは「コマンドラインを起動した・コマンドを実行する度」ということになります。

使い方の違いで示しましたが、サブコマンドdeleteは複数の削除候補が出てくると、全て削除するか一部を削除するか聞いてくるので、上記のfunctionは場合によっては鬱陶しいんじゃないかなぁと個人的には思っています。(この挙動は上記functionにオプション--exactを付与すると改善されるかもしれません)

(2) https://stackoverflow.com/questions/34001591/in-fish-is-it-possible-to-ignore-all-git-related-commands/34015787

〜横道〜 aliasの話で少し触れたabbrコマンドを使って、保存しておきたくないコマンドの先頭に空白を付与して別名保存します。

abbr -a history ' history'

先頭に空白を記述すると、履歴に保存されないというfishの挙動を利用した形です。
設定するならこちらの方がいいのではないかと思います。
参考サイト(2)の最後に書かれている関数については(1)と同じ理由で、個人的には非推奨です。

ちなみに
逆に、重複するコマンドの履歴も保存できるようにする術はなさそうです。

個人的には、重複するとは言え、「いつごろ、このコマンド使ったっけ?」というのを調べることもあるんじゃないかなぁ、、と思うのですが、調べた限りは見つかりませんでした。

history expansion について

設定とは少し違いますが、、history expansionについて、
history expansionとはbashなどで![行番号]と指定すると、historyの行番号に該当するコマンドが実行される、あの機能のことです。(!!打つと、直前のコマンドが実行されたり、、詳しくは→https://mseeeen.msen.jp/bash-history-expansion/)

fishには![行番号]の記述方法は・・・ない!(そもそもhistoryに行数ついてない・・・)

これは公式のFAQでも、言及されています。(すごくfish側のこだわりを感じるところだと、勝手に思っています)
Why doesn't history substitution ("!$" etc.) work?
(日本語版)履歴置換(!$など)はなぜ動かないの?

「ないんだけど、fishは対話的に履歴を探し出すことに秀でているから、そっちの機能を使ってね!」っという感じ。

fishを愛用されている方にはお馴染みなのかもしれないが、
例えば、コマンドの一部を入力して上矢印を押すと、記述したコマンドに限定して履歴を遡れたりします。(cdコマンド等で試してもらうと、極めて強力だということが分かるのではないでしょうか)使い慣れると![行番号]よりもはるかに便利だと、私は思っています。

どーーーしても!を使いたい場合は、sudo !!に対してfunctionで設定する方法がissueに紹介されています。(sudoつけ忘れたコマンドにsudoをつける用途)
https://github.com/fish-shell/fish-shell/issues/288

function sudo
    if test "$argv" = !!
        eval command sudo $history[1]
    else
        command sudo $argv
    end
end

ちなみに、公式のFAQには上矢印一回して、homeボタン押してからsudo書けばいいじゃないか!、と書いてあります。

「ユーザーフレンドリ」なものに悪いものはいない?

先ほどから度々参照している、全訳!fishシェル普及計画のサイトに設計理念というメニューがあり、設計原則の一つにこんなことが書かれています。

「設定可能性」は諸悪の根源
プログラム内のすべての設定オプションはユーザが本当に求めているものを理解するにはあまりにも馬鹿げている場所です。 また、設定オプションはプログラムとそれを実装したプログラマの双方の障害を考慮すべきです。

「根拠」ではさまざまな設定オプションをメンテナンスするのは悪夢のように困難です。と続きます。

私が何を言いたいのかというと、上記で長々と書いたbashでよくやる設定についての内容は、
真っ向からこの原則に反発した形であり、
本当にfishで設定する必要があるのか否か、一考する余地がある、、ということです。

以下にbashでよくやる設定についてで調べた内容について、個人的に思う「設定しなくていい理由」を書いてみました。

○ コマンド履歴の保存件数を増やす
(設定する方法すらありませんが・・)デフォルトで256k個もの件数が保存でき、また「重複するコマンドの履歴は保存されない」デフォルトの挙動で保存される履歴も限られている中で、これ以上件数を増やす設定は必要でしょうか?

○ historyに日時を付与する
元々、-t / --show-timeのオプションで、日時の表示・非表示が自在に変更できる状況で、aliasに設定する必要はどこまであるでしょうか?

○ 特定のコマンドの履歴を保存しない
無理に設定しなくてもサブコマンドのdeleteを使えば、必要のない履歴はすぐ消すことができます。また、よく必要ないと目される決まったコマンドは「重複するコマンドの履歴は保存されない」の影響で、1つしか登録されない場合が多いはずです。(一つでも登録されるのが気持ち悪いという場合は必要かもしれません)

○ history expansion について
慣れたらfishの補完の方が便利だと思います。設定してしまうと、fishの良さを蔑ろにしてしまう節があります。

原則を鑑みると、そもそも、(私自身そうでしたが)使いやすくするために、設定ファイルを編集しないといけない!!という発想自体がfishにおいては間違っていると言えそうです。
少なくともbashで設定していたからとfishでも設定すべきかどうかは、甚だ疑問です。(耳が痛い・・・)

「ユーザフレンドリ」なfishは、ガチガチに自分好みに使いたい!!という人には、もしかするとにもなり得るのかもしれません。

fishは「いいぞ」ユーザフレンドリは「いいぞ」という記事が多いですが、historyの使い方を通して、場合によっては不便な一面もあるのかなぁ、という参考資料になってくれたら幸いに思います。(fishに乗り換えようかと検討されている方の一助になれば・・・)

何かしら設定する方法

語弊がありそうなので蛇足を書きますが、もちろん、何も設定してはいけない、というわけではないと思います。
「他のシェルほど自分好みに設定できないところがあるけど、我慢してね。一応デフォルトで使いやすくしているつもりだよ」くらいだと思います。
何か設定するときは、
fish shellが結構良かった話でも紹介されている。

fish_config

を使うと、ブラウザでグラフィカルに設定をいじることができます。

最後に

サブコマンドのsearchdeleteは便利なので、知らなかった人は覚えておいて損はないと思います。
また、fishで何かしら「設定しよう」と思ったときは、まずはfishがデフォルトでどんな挙動をしているのか調べるのがおすすめです。(調べてみると、今回のようにそれなりに考えられた挙動だったりします)

fishについて批判的な文も書きましたが、最終的にはとても使いやすい!!と思っています。(特に補完機能)
なんだかんだ、これからも使っていく所存です!!

参考

fishについて
公式サイト
→ 特に 公式サイトのhistoryのページ
全訳!fishシェル普及計画
→特に 全訳!fishシェル普及計画のhistoryのページ
fish-shellのGithubのページ

bashhistoryについて
コマンド履歴を表示するhistoryコマンドを詳しく!【Linuxコマンド集】
コマンド履歴の達人を目指してみる

17
14
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
17
14