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

cdコマンド改造記事第3回です。
[前回の記事：ご注文はHOMEに帰らないcdですか？](https://qiita.com/Riliumph/items/7675f97027e7fc82ff41)

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

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

# TL;DR;

僕のbash設定は、もう改造に改造を重ねてこんな簡単なbashではなくなりました。
気になる人は読んで見てください。

気が向いたら解説記事でも書きます。

[bash設定](https://github.com/Riliumph/bash)

# 第3羽「楽するアイテムは**bind**と**readline**と**スクリプト**」

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

``` bash:terminal
$ cd ../../../../.../
```
って連打しませんか？

これって、

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

という<font color=red>**スーパープレイが必要**</font>な訳ですが。
まぁ、控えめに言って<font color=red>**無理ゲー**</font>ですよね

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

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

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

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

### やってみよう

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

```bash:.bash_aliases
# 暫定的にCtrl+fを使用する
bind '"\C-f":"cd ../\n"'
```
`. ~/.bashrc`でbashを立ち上げ直しましょう。
そうすると**Ctrl+fを押すだけで`cd ../`が実行される**ようになります。
こんな感じですね。

![bind.gif](https://qiita-image-store.s3.amazonaws.com/0/54925/bb549a8f-b170-060b-9654-8943203232b6.gif)

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

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

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

## アイテムその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という奴なのでこれとは違います。ご注意を。

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

```bash:terminal
$ bind -p
```

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

```bash:terminal
$ bind -l
```

### やってみる前に準備

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

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

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

### やってみよう

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

```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](https://qiita-image-store.s3.amazonaws.com/0/54925/59fffefd-0b80-6634-04f8-1d3a05d57739.gif)

## アイテムその3：組み合わせてスクリプトにしてみよう

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

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

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

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

```bash:.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というパスなどを表示している文字列のこと。
> よかったら[ここ](https://qiita.com/Riliumph/items/dffcac152407da686ebd)読んで
> revert-lineを入れている理由は、時々PS1が表示されないことがあったからです。

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

![comp.gif](https://qiita-image-store.s3.amazonaws.com/0/54925/a090a0f0-a943-da4f-5c7e-aeea6b6f1d75.gif)

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

## 謎の呪文「<font color="red">\e[3;5~</font>」とは(｡ωﾟ)？(ﾟ｡ 3 )？(ﾟω｡)？(ε｡ﾟ )？

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

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

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

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

```bash:terminal
$ cat
```

![cat.gif](https://qiita-image-store.s3.amazonaws.com/0/54925/e10515c7-2976-705c-1ff7-e6d39d6a4e31.gif)


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

ホントはね？
こんな感じにbindコマンドにreadline関数を指定できるみたいなんですけど……
なんかフツーにエラーになったんですよね(´･_･`)ｺﾏﾝﾄﾞｶﾞ,ﾐﾂｶﾘﾏｾﾝ

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

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

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