VSCode+WSL で Bash のコーディング環境を整える

# はじめに

VSCode と WSL を使って Bash のコーディング環境を構築する手順を書きました。
使用するツール群は以下と通りです。

| 機能           | ツール                                                                                                                                            |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| 実行環境       | WSL Ubuntu 24.04                                                                                                                                  |
| エディタ       | VSCode<br>→ WSL に接続するために WSL 拡張機能も必要。                                                                                             |
| LSP            | Bash IDE<br>→ VSCode 拡張機能であり、別途インストールした shfmt, ShellCheck と連携可能。ただし、これらがないと LSP が使えないというわけではない。 |
| フォーマッター | [shfmt](https://github.com/mvdan/sh) (`go install` でインストール)                                                                                |
| 静的解析ツール | [ShellCheck](https://github.com/koalaman/shellcheck) (`apt install` でインストール)                                                               |
| デバッガ       | Bash Debug<br>→ VSCode 拡張機能、 [bashdb](https://github.com/Trepan-Debuggers/bashdb) が同梱されている。                                         |

:::note info
本当は [explainshell](https://github.com/idank/explainshell) も Bash IDE と連携しているのでこの記事で紹介しようとしましたが、手元でセットアップが失敗したので止めました。ただ、セットアップしたところで、ローカルに Python の Web サービスを起動させないといけないので、ちょっとオーバーエンジニアリングな気がしているのと、 https://www.explainshell.com/ にて利用できるので、そこまで残念ではないです。
:::

# 環境構築

では、上記の開発環境を順に沿って説明していきます。

## 1. WSL2 のインストール

Windows 10 ビルド 19041 以上、または、Windows 11 であれば、以下のコマンドだけで大丈夫です。

```powershell:PowerShell(管理者モード)
# OS のバージョンを確認
$PSVersionTable.OS
#=> Microsoft Windows 10.0.19045

# WSL をインストール
wsl --install

# インストールが完了すれば、wsl コマンドで Ubuntu を起動 & ログインする。
wsl
# 初回はユーザ名とパスワードを設定する。
```

これで Ubuntu ディストリビューションが WSL として利用できます。

それ以外の PC 環境の方は、以下の公式ページをご参照ください。

https://learn.microsoft.com/ja-jp/windows/wsl/install

## WSL に Docker をインストール

explainshell を Docker で起動するため、先に Docker をインストールします。 explainshell は不要という方はスキップしていただいても大丈夫です。
WSL にログイン後、 `docker run hello-world` をすぐに実行できる状態までセットアップします。

```bash:bash on WSL
# Docker 公式が提供しているセットアップスクリプトを実行する
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

# sudo なしで docker コマンドを実行できるようにする
# `sudo groupadd docker` は既に docker ユーザが上記のコマンドで自動作成されるため不要
sudo usermod -aG docker $USER
newgrp docker

# Ubuntu では Dcoker サービスはデフォルトで起動時に開始されるため、以下のコマンドは不要です
# sudo systemctl enable docker.service
# sudo systemctl enable containerd.service
```

:::note info
Docker をアップグレードしたい場合は APT を使って更新できます。

<details><summary>Docker の更新手順</summary>

```bash:bash on WSL
# 利用可能なバージョンを確認
apt-cache madison docker-ce | awk '{ print $3 }'
#=> 5:27.4.0-1~ubuntu.24.04~noble
#=> 5:27.3.1-1~ubuntu.24.04~noble
#=> ...

# バージョンを指定してアップグレードする
VERSION_STRING=5:27.4.0-1~ubuntu.24.04~noble
sudo apt-get install \
    docker-ce=$VERSION_STRING \
    docker-ce-cli=$VERSION_STRING \
    containerd.io \
    docker-buildx-plugin \
    docker-compose-plugin
```

</details>
:::

他のインストール方法や最新のインストール方法を確認したい場合はこちらをご参照ください。

[dockerdocs - Install Docker Engine on Ubuntu](https://docs.docker.com/engine/install/ubuntu/)
[dockerdocs - Linux post-installation steps for Docker Engine](https://docs.docker.com/engine/install/linux-postinstall/)

## VSCode のセットアップ

VSCode のインストール手順は割愛させていただき、拡張機能のインストールから記載します。まず、 WSL 環境で VSCode を利用可にするため、 WSL 拡張機能をインストールします。

https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl

## Bash IDE

次に Bash の LSP 実装である Bash IDE をインストールします。

https://marketplace.visualstudio.com/items?itemName=mads-hartmann.bash-ide-vscode

以降は Bash IDE の最低限の機能を利用する上では必須ではないですが、より快適な環境を作るために Bash IDE を連携可能なツールを３つほどインストールします。

### shfmt のインストール

フォーマッターである shfmt をインストールします。

```bash:bash on WSL
# Go のインストール（shfmt で必要のため）
sudo apt install -y golang
# shfmt のインストール
go install mvdan.cc/sh/v3/cmd/shfmt@latest

# $HOME/go/bin（$GOPATH が定義済みの場合は、 $GOPATH/bin）を PATH に追加
vi ~/.bashrc
# export PATH=$HOME/go/bin:$PATH

source ~/.bashrc
```

### ShellCheck のインストール

静的解析ツールである ShellCheck をインストールします。

```bash:bash on WSL
sudo apt update

# shellcheck のインストール
sudo apt install -y shellcheck
```

こちらで、 LSP に関するセットアップは完了です。

## Bash Debug

続いて、デバッガをセットアップしていきます。
まずは、Bash Debug の拡張機能をインストールします。

https://marketplace.visualstudio.com/items?itemName=rogalmic.bash-debug

[bashdb](https://github.com/Trepan-Debuggers/bashdb) を内部で使っていますが、 拡張機能に同梱されているため別途インストールは不要です。

Bash Debug にてデバッグ実行をするには、VSCode 設定ファイルである `launch.json` を作成する必要があります。
ここでは、デモ用のプロジェクトを作成し、それをフォルダで開き、その中で `launch.json` を作成します。

```bash:bash on WSL
mkdir bash-demo
code bash-demo
```

WSL で code コマンドにパスを指定して実行することによって、WSL 上の指定のフォルダを開いた状態で VSCode が立ち上がります。

VSCode 起動後は、 .vscode/launch.json の場所に以下の内容を記載します。

```json:.vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "bashdb",
            "request": "launch",
            "name": "Bash-Debug (simplest configuration)",
            "program": "${file}",
            "cwd": "${workspaceFolder}",
            "args": []
        }
    ]
}
```

以上で Bash の環境構築は終わりです。

# 使い方

ここからは実際にどういう機能が利用できるか軽く紹介します。
デモ用に簡単な Bash スクリプトを以下のように書きました。

```bash:demo.sh
# メッセージを装飾する関数（show_calc と show_date から使用される）
decorate() {
    echo "=== $1 ==="
}

# 計算結果を表示する関数
show_calc() {
    local -r result=$(($1 * $2))
    decorate "計算結果: $result"
}

# 日時情報を表示する関数
show_date() {
    local -r current_time=$(date "+%H:%M")
    decorate "現在時刻: $current_time"
}

# メインの処理
show_calc 6 7
show_date
```

## Bash IDE 本体の機能

上で記載した Bash をエディタで開くと以下のようにシンタックスハイライトされた状態で表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/1126ddd4-048e-191a-8cd5-37d4c7897797.png)

また、 echo コマンドなどにマウスオーバーするとコマンドのヘルプも確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/2c895e89-f4fa-ba1d-7b9b-a399a6b011f8.png)

また、以下のような基本的な機能は他の言語と同じように初期設定のまま利用できました。

- 定義への移動（F12）
- 参照箇所の検索（Shift+Alt+F12）
- コード補完（定義済みの変数や関数、Bash や Linux のコマンドなど）
  → 変数についてはスコープ外の変数は補完に出ない
- 変数名や関数名のリネーム

### shfmt

Alt + Shift + F でフォーマットされます。
もし、フォーマットされない場合は、 shfmt コマンドが PATH に登録されていない可能性があるので、その辺りをご確認ください。

### ShellCheck

こちらも特に設定不要で、自動で静的解析が実行され、問題のある個所があれば波線で指摘を入れてくれます。
試しに、未使用の変数を定義してみると、以下のように表示されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/65b2580b-98fd-d312-36c1-d5ba5e1ed3d7.png)

## Bash Debug

ブレークポイントを貼って、 F5 でデバッグ実行をするとステップ実行が開始できます。 F11 で関数に対してステップインも可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/967aee5c-e997-5d08-8d95-c8990d8baa0f.png)

ただ、なぜかローカル変数の `result` が VARIABLES の変数一覧に表示されませんでした。この辺りの挙動はちょっと謎です。

上記の例はサブシェルを使っていたのが、もしかしたら原因かもしれないです。以下のように値を代入した変数の中身であれば、マウスオーバーで確認できました。ただ、これもサイドバーの VARIABLE の変数一覧には表示されないようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114728/bbf4f7fd-8be6-5640-7a04-28fe27bc2f64.png)

# おわりに

Bash を書くときこれがないとキツイと言えるまでではなかった印象ですが、「ひぇー、なんか複雑な Bash スクリプトに遭遇して、なんとかメンテしないといけない立場に陥ってしまったーっ」という状況になったときは、こういう環境を準備して挑みたいです。