Bash

ご注文は親ディレクトリに戻ることですか?

cdコマンド改造記事第3回です。
前回の記事:ご注文はHOMEに帰らないcdですか?

今まで、cdコマンドで移動することに対して色々と手を入れてきたわけですよ。
まだcdコマンドは弄りますけど、ちょっと違うこともやってみましょう。

今回はちょっと長いです……。

第3羽「楽するアイテムはbindreadlineスクリプト

cdコマンドで一個上のディレクトリに戻るとき

terminal
$ cd ../../../../.../

って連打しませんか?

これって、

  • 宛先のディレクトリが、どれだけ離れてるか分かっていないといけない
  • 正しく離れている数だけ../を入力しなければならない

というスーパープレイが必要です。

どうスーパープレイかって言うと

  • 戻り先のディレクトリが、どれだけ離れてるか分かっていないといけない
    パスを指さし確認して数えないと分からないですよねー
  • 正しく離れている数だけ../を入力しなければならない
    ../をバカみたいな数だけタイプとかメンドクセーですよねー
    頑張っても、↑みたいにドットが3つになって「そんなディレクトリねぇから!」って怒られるんですよねー
    そんで、再入力とかマジふぁっきゅー(キ〇ナアイ風)。

まぁ、控えめに言って無理ゲーですよね

その点、さすが!!
Windowsさんのエクスプローラ!
「Alt+↑」キーで親ディレクトリに戻っていくではありませんか!

これだ!!と思ったので、それを実装していきたいと思います。

アイテムその1:bindコマンド

キーバインドを設定するコマンドだと思ってたんですけど、
--helpを見る限りでは、なんかソケットファイル?まで操作できるらしいですね。
詳しいことは知りませんけど(´・_・`)

やってみよう

とりあえず、これでキーバインドしてみましょう。

.bash_aliases
# 暫定的にCtrl+fを使用する
bind '"\C-f":"cd ../\n"'

. ~/.bashrcでbashを立ち上げ直しましょう。
そうするとCtrl+fを押すだけでcd ../が実行されるようになります。
こんな感じですね。

bind.gif

これでcd ../../../なんていうメンドクサイ無理ゲーコマンドから卒業出来ました!
Ctrl+fを連打するだけで好きなだけ親ディレクトリを辿れますよ!!
やったー!!

課題

でも、一つ問題があります。
最後、コマンドエラーになってますね。
これは単純にctrl+fでcd ../\nコマンドを入力しているだけなので、すでにターミナルに文字列が入力されていると使えません

この問題を解決してみましょう。

アイテムその2:readline

GNU readlineってのは、terminal上でemacsみたいな動きをさせるライブラリ(?)だそうです。
こんなのがデフォルトで設定されています。
めちゃくちゃ設定されているのでちょっとだけ書きます。

コマンド readline関数 意味
c-l clear-screen 画面をクリアする
c-k kill-line カーソル以降の文字列を削除
c-u unix-line-discard カーソル以前の文字列を削除
c-a beggining-of-line カーソルを先頭に移動
c-e end-of-line カーソルを末尾に移動
c-x c-r re-read-init-file 設定ファイルをリロード

画面が何が何だか分からなくなってきたらC-lで綺麗にしたり、
パスワード打ち間違えたらc-uで全消ししたり、
よくやりますよねー。
※C-cでシグナルを送信するのは、sttyという奴なのでこれとは違います。ご注意を。

もっと知りたい人は下記コマンドで現在の設定状況が見れます。

terminal
$ bind -p

どんな関数があるのか知りたい人は下記コマンドで見れます。

terminal
$ bind -l

やってみる前に準備

設定自体は$HOME/.inputrcに書くらしいです。
でも、環境変数INPUTRCを定義してあげればそっちを見に行ってくれるようです。

.bashrc
#INPUTRC="あなたの好きなパス"
INPUTRC="$HOME/.config/readline.inputrc"

私、.inputrcだけだと何のファイルだったか忘れちゃうんですよ。
なのでreadline.inputrcなんて名前にしてますけど、好きなのにしてくださいね。
それから、bashをリロードして設定を反映しておきましょう。

やってみよう

一単語ずつジャンプするカーソル移動を作ってみます。

readline.inputrc
$if bash
# readlineは他のプログラムも設定を読みに来るので、bashで分岐しておきます。
  "\e[1;5C": forward-word   # Control+Right
  "\e[1;5D": backward-word  # Control+Left
$endif

c-x c-rを入力するとreadlineが再設定されます。※ダメだったらbashを立ち上げ直してください。
そうしたらこんな感じで動きますね。
jump_word.gif

組み合わせてスクリプトにしてみよう

これでパーツは揃いました。
gnu readlineをbindしてやれば良さそうです。

でも、どんな関数を使いましょうか?
ctrl+uはカーソル以前の全文字列の削除
ctrl+kはカーソル以後の全文字列の削除
です。
ctrl+u ctrl+kと連続して実行してあげても問題はありませんが、今回は入力されている文字列を全削除するreadline関数を使ってみます。

readline.inputrc
$if bash
"\e[3;5~": kill-whole-line # Ctrl+Del
$endif

次に、これを使ってbindの設定をしてみましょう。

.bash_aliases
# \e[1;3A => Alt+↑
# \e[3;5~ => Ctrl+Del(Assign: kill-whole-line)
# \er     => Ctrl+r  (Assign: revert-line)
bind '"\e[1;3A":\e[3;5~\er cd ../\n'
# Ctrl+u Ctrl+kでもいけるハズ
# bind '"\e[1;3A":\C-u\C-k cd ../\n'

やっていることは

  1. kill-whole-lineで全文字列を削除
  2. revert-lineでPS1(※1)を再描画
  3. cd ../を実行

※1 PS1とは
prompt string1というパスなどを表示している文字列のこと。
よかったらここ読んで
revert-lineを入れている理由は、時々PS1が表示されないことがあったからです。

これでうまく動くことでしょう!

comp.gif

適当に文字列が入ってても問題なく実行できています!!
やりました!

謎の呪文「\e[3;5~」とは(。ω゚)?(゚。 3 )?(゚ω。)?(ε。゚ )?

最初に「いったい何の呪文だ?」と思った人だけ読んでください。
あんまり知らないんですけど、エスケープシーケンスというらしいです。

今回使ったものをまとめておきます。

Escape Sequence 押下するボタン
\e[3;5~ Ctrl + Del
\e[1;5C Ctrl + →
\e[1;5D Ctrl + ←
\e[1;3A Alt + ↑

catコマンドのあとに入力すると見ることができます。

terminal
$ cat

cat.gif

まとめ

今回は、bindコマンドとGNU readlineを使って楽にcd ../を実行できるようにしてみました。
ほとんどが浅い知識で出来ているので、間違ってたら教えてください。

ホントはね?
こんな感じにbindコマンドにreadline関数を指定できるみたいなんですけど……
なんかフツーにエラーになったんですよね(´・_・`)コマンドガ,ミツカリマセン

.bash_aliases
bind '"\C-f": forward-word' # OK
bind '"\C-f": kill-whole-line revert-line cd ../\n' # NG

なので、.inputrcを使ってみたら回避できました。

それでは、今回はここで終わりです。