[Bash] プロンプトと制御文字とbashスクリプト

私は私用のPCに愛するfishを使っていたのですが、
残念ながら、私の世界はbashにあふれた世界だったのです。

そこで、使いにくい`bash`のプロンプト文字列を`fish`のように変更しましょう！！っと思って設定するまでの経緯を書きました。
具体的には、
- プロンプトに色を付けましょう
- gitのbranch名を表示しましょう

### プロンプトの文字列変数
- - -
bashrcファイルの中にこんな変数があります。

``` bash:.bashrc
PS1='\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
```

これがプロンプト文字列を制御しています。
これをいじくり倒しましょう。

しかし、これを最初に見たときの感想はこうでした。
「はぁ？？なんなんだよ、この意味不明な文字列。意味わかっても可読性0じゃねぇか」

これを生で書くのは、私には無理です。
むしろ、人間がやることではないとすら思っています。
この設定を生でスラスラやる人たちは、変態だとしか思えません。

そこでシェルスクリプトを使って可読性を上げてみようと思います。
適当な値を引数に放り込むと制御シーケンスの文字列を返す関数でも作ればいいでしょう。
メンドウで難しいけどルールがあるものはシステムに作らせればいいんです。

### bash関数の戻り値
- - -
```bash:.bashrc
MakeCtrlSequence(){
  local font_type=$1
  local fg=$2
  local bg=$3
  case $# in
    1) return "\[\e[${font_type}\]";;                 #ERROR
    2) return "\[\e[${font_type};${fg}m\]";;          #ERROR
    3) return "\[\e[${font_type};${fg}m\e[${bg}m\]";; #ERROR
  esac
}
```

制御シーケンスは必ず\[\]はセットで書きましょう。
よく`\e[0m`などと書かれたサイトを見受けられますが、その場合はいくらかの文字列がターミナル上に残ってしまいます

> [How do I get long command lines to wrap to the next line?](https://askubuntu.com/questions/24358/how-do-i-get-long-command-lines-to-wrap-to-the-next-line)
> 経験したものとして、`(*'_') $ docker push`を実行しようと↑キーで履歴を遡っていたら`(*'_') $ docker p`の文字列が張り付いてしまいました。
> 上記サイトを参照して必ず`\[`と`\]`が対応が必要であることを学びました。

bashのreturnコマンドは引数にエラーコードとしての「0～255」までの値しか設定できません。
つまり、関数として返せるのは1byte範囲の数値だけで文字列を返すことはできません。
ここでも「はぁ？？使えねぇなシェルって」って思いました。
でも解決策はありました。

### bashにおける文字列の返し方
- - -
答えを言うと、標準出力で返します！
これを見たときは、素直に「すげぇ！」と思いました。
それと同時に、「既存の自作コードで`$(pwd)`とかで受け取ったりしてるじゃん。気づけよ、俺」と思いました。

こうなります。

```bash:.bashrc
###
# MakeCtrlSequence
# 書式・前景色・背景色の制御シーケンスを生成します。
# 引数の数でしか判断しません。引数内容に対するエラー処理はありません。
# 使う際は注意深く、そして正しく。
# @param $1 Fontに対する書式コード
# @param $2 Fontの前景色コード
# @param $3 Fontの背景色コード
MakeCtrlSequence(){
  local font_type=$1
  local fg=$2
  local bg=$3
  case $# in
    0) echo 'Missing args'; return 1;;
    1) echo "\[\e[${font_type}\]"; return 0;;
    2) echo "\[\e[${font_type};${fg}m\]"; return 0;;
    3) echo "\[\e[${font_type};${fg}m\e[${bg}m\]"; return 0;;
    *) echo 'Too many args'; return 1;;
  esac
}
```

### スクリプトの最終形
- - -
こんな感じのスクリプトになりました。

```bash
### 制御シーケンスを作ってもらうよ！
MakeCtrlSequence(){
  local font_type=$1
  local fg=$2
  local bg=$3
  case $# in
    0) echo 'Missing args'; return 1;;
    1) echo "\[\e[${font_type}\]"; return 0;;
    2) echo "\[\e[${font_type};${fg}m\]"; return 0;;
    3) echo "\[\e[${font_type};${fg}m\e[${bg}m\]"; return 0;;
    *) echo 'Too many args'; return 1;;
  esac
}
# プロンプト文字列を作ってもらうよ！
MakePS1(){
  local DEBIAN_INFO=${debian_chroot:+($debian_chroot)}
  # COLOR
  local white=$(MakeCtrlSequence 00 37)
  local bold_lame=$(MakeCtrlSequence 01 32)
  local yellow=$(MakeCtrlSequence 00 33)
  local bold_red=$(MakeCtrlSequence 01 31 43)
  echo "${DEBIAN_INFO}${bold_lame}\u${white}@\h:${yellow}\w\n${white}$ "
}
# ここで設定するよ！
PS1=$(MakePS1)
```

bashを色々と勉強しました。難しいですね。


### Gitのブランチ名を表示する
- - -

この時のgitのversion忘れました……

これをダウンロードしてください。
https://github.com/git/git/blob/master/contrib/completion/git-prompt.sh
https://github.com/git/git/blob/master/contrib/completion/git-completion.bash

それから実行権限を付与します。

``` 
$ chmod a+x git-prompt.sh
$ chmod a+x git-completion.sh
```




あとは、読み込んで設定するだけです。
bashでもお手軽ですね。

```bash:.bashrc
### Add git-prompt
source $BASH_ROOT/git-completion.bash
source $BASH_ROOT/git-prompt.sh

# プロンプトに各種情報を表示するよ！(1 or null)
GIT_PS1_SHOWDIRTYSTATE=1
GIT_PS1_SHOWUPSTREAM=1
GIT_PS1_SHOWUNTRACKEDFILES=1
GIT_PS1_SHOWSTASHSTATE=1

# 制御シーケンスを作ってもらうよ！
(省略)

# プロンプト文字列を作ってもらうよ！
MakePS1(){
  local DEBIAN_INFO=${debian_chroot:+($debian_chroot)}
  local GIT_BRANCH='$(__git_ps1)'   # must use single-quotation

  # COLOR
  local white=$(MakeCtrlSequence 00 37)
  local B_lame=$(MakeCtrlSequence 01 32)
  local yellow=$(MakeCtrlSequence 00 33)
  local I_red=$(MakeCtrlSequence 03 31)
  echo "${DEBIAN_INFO}${B_lame}\u${white}@\h:${yellow}\w${white}|${I_red}${GIT_BRANCH}\n${white}\$ "
}
# ここで設定するよ！
PS1=$(MakePS1)
```
![2018-07-27.jpg](https://qiita-image-store.s3.amazonaws.com/0/54925/265af35e-4cb8-b79f-9125-515057d8b2fb.jpeg)

うーん。fishみたいにgit statusの内容で<font color="Red">赤</font>とか<font color="DarkKhaki">黄色</font>とかにできないもんですかね？
git statusの内容をsed awkとかして切り替えたら実現できそうですけど……

違うbranchで作業した挙句、間違ったbranchへpushすることがなくなったので、今はOKということにします。

すいません。。。