Docker コンテナ内シェルのカラー設定

## モチベーション
DockerコンテナにSSH接続した際、基本的にカラー設定は無効化されている。無色だと作業効率が落ちるのでカラー化したいが毎度プロンプトや `ls` や `grep` などの各種色設定をログインする度に設定するのは骨が折れる。共有のプロジェクトなどは勝手に `Dockerfile` にカラー設定を書き込むことが出来ない場合も多い。

そこで `docker exec` でSSH接続する度に色設定情報を送り込むようにすればどのコンテナに接続しても好きな色設定 (+その他設定) でコンテナ内作業ができるので **捗ること請け合いだ！**

## 方針

1. カラー設定が書かれた `.bashrc` をホスト側に用意する
2. ホストの `.bashrc` をコンテナ内にコピーする
3. コンテナに環境変数 `TERM=xterm-256color` を設定する
4. 上記をワンコマンドで実行するシェル関数を定義する

## プロンプトのカラー設定

bash なら `$PS1` zsh なら `$PROMPT` にそれぞれプロンプトに表示する情報と色の設定を書き込む。

```bash:.bashrc
# 例
case "$TERM" in
    xterm-color|*-256color) color_prompt=yes;;
esac
if [ "$color_prompt" = yes ]; then
    # 色設定あり
    PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
    # 端末の256色設定が無効なら色コードは入れない
    PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset
```

> 参考:
> - [Bashのプロンプトをカスタマイズする【色変・時間表示】](https://zenn.dev/kcabo/articles/555d9cc6dad0c3)
> [Mac: ターミナルとプロンプトをカスタマイズする(zsh) - connecting the dots](https://ktksq.hatenablog.com/entry/mac-customize)

## `ls` `grep` のカラー設定

以下を `.basrhc` に追記しておきます。

```bash:.bashrc
if [ -x /usr/bin/dircolors ]; then
    test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
    alias ls='ls --color=auto'
    alias grep='grep --color=auto'
    alias fgrep='fgrep --color=auto'
    alias egrep='egrep --color=auto'
fi
```


## ホストの `.bashrc` をコンテナ内にコピーする

`docker cp` コマンドを使ってホスト内のファイルをコンテナにコピーすることができる。
Docker では基本的に root ユーザーでのログインになるので root ユーザーのホームディレクトリである `/root/` 以下に `.bashrc` をコピーする。

> [書式] **docker cp `ホストのファイルパス` `コンテナ名:コンテナ内のパス`**

```bash
# 例:
# my_service イメージから my_service という名のコンテナを立ち上げる
docker run --rm -d -p 3000:3000 --name my_service my_service

# ホストの .basrc を コンテナの /root/ にコピーする
docker cp ~/.bashrc my_service:/root/
```

> 参考:
> - [動作中のDockerコンテナにHostからファイルを転送する - Qiita](https://qiita.com/hidetarou2013@github/items/6ceae8e547910f6416f8)

## コンテナに `TERM=xterm-256color` を設定する

環境変数 `$TERM` に `xterm-256color` が設定されているとターミナル内で扱える色が256色になります。
これをコンテナ内にログインするときに潜り込ませます。

```bash
docker exec -it my_service env TERM=xterm-256color /bin/bash
```

docker引数の `--env` ではなくシェルの `env` で設定しています。

## コマンド一発でカラーシェルにログインする！

上記設定を一つの関数にまとめ一発で実行できるようにします。
`~/.bashrc` に以下関数を書き込む。
（【追記】→ `~/bin/` のパスを通して `~/bin/docker-ssh` というシェルスクリプトを作ったほうが良さそう（※コメント参照） ）
```bash:.bashrc
docker-ssh() {
    cmd1="docker cp ~/.bashrc $1:/root/"
    echo $cmd1 && eval $cmd1 && echo 'Copied! .bashrc'
    cmd2="docker exec -it $@ env TERM=xterm-256color /bin/bash"
    echo -e "\n" $cmd2 && eval $cmd2
}
```

使い方は、立ち上げたコンテナ名が `my_service` の時に以下を実行するだけです。

```bash
$ docker-ssh my_service
docker cp ~/.bashrc web:/root/
Copied! .bashrc

docker exec -it web env TERM=xterm-256color /bin/bash
```
↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27532/eda46478-c20e-34b5-52d8-8bd2975c367b.png)

## docker compose の場合

`docker compose` で起動しているコンテナにSSH接続する際も基本的に同じ。間に `compose` という文言が入るだけ。

```bash=
# .bashrc に以下追記
docker-compose-ssh() {
    cmd1="docker compose cp ~/.bashrc $1:/root/"
    echo $cmd1 && eval $cmd1 && echo 'Copied! .bashrc'
    cmd2="docker compose exec -it $@ env TERM=xterm-256color /bin/bash"
    echo -e "\n" $cmd2 && eval $cmd2
}
```


```bash=
# composeでコンテナ起動
docker compose up -d
```

```bash
# コンテナの起動状況を確認
$ docker compose ps

NAME         IMAGE      COMMAND                 SERVICE  CREATED        STATUS       PORTS
myapp-db-1   myapp-db   "docker-entrypoint.s…"  db       5 months ago   Up 5 hours   0.0.0.0:15432->5432/tcp
myapp-web-1  myapp-web  "/app/docker/web/ent…"  web      4 days ago     Up 5 hours   0.0.0.0:13000->3000/tcp

#=> 「web」 がサービス名
```


```bash
# webコンテナにログイン
docker-compose-ssh web
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27532/a684c4fb-2259-ea64-bb14-6d094a5cf9c4.png)



## （余談）引数を変数にする

コマンドラインのオプション引数でよく使うものはグローバル変数化しておいたほうが何かと使い勝手いいかなと思って最初は以下のようにしていた。

```bash:.bashrc
docker-ssh() {
    export DOCKER_EXEC_OPTIONS='-it --env TERM=xterm-256color'

    docker cp ~/.bashrc $1:/root/
    docker exec $DOCKER_EXEC_OPTIONS $@ /bin/bash
}
```

しかし、これだと以下のようなエラーになりダメだった。

```bash
$ docker-ssh web
unknown shorthand flag: ' ' in - --env TERM=xterm-256color
```

シェルが文字列を解釈する順序(?)的な問題かなと思って色々試すのが面倒になったので `eval` にしたらうまく行った。

```bash:.bashrc
docker-ssh() {
    export DOCKER_EXEC_OPTIONS='-it --env TERM=xterm-256color'

    docker cp ~/.bashrc $1:/root/
    eval "docker exec $DOCKER_EXEC_OPTIONS $@ /bin/bash"  # eval で文字列をコマンドとして解釈
}
```

`eval` はよくわかってないですがあまりよろしくないようなので、使わずにやりたいところ・・・。

わかる方教えてくださいませ！
↓
【追記】コメントにて詳しく教えて下さいました！