コマンド一つでテンプレートから環境を構築する為のシェルスクリプトを書く

例えばTypeScriptなどで書き殴りのコードを書きたい時、
自分の場合、`yarn init -y`して`yarn add -D typescript @types/node`して、`index.ts`を作って`tsconfig.json`を適当に作って書き換えて、`package.json`にnpm-scriptsを書き足して・・・みたいな事をしています。
前に書いたコードを引っ張ってきたら良いじゃんって言うのはごもっともですが、無駄なパッケージが削ぎ落とされたコードを探したり編集したりするのはめんどくさいです。

環境構築の為のツールは公式、サードパーティ問わずたくさん存在しているのでそちらを使っても良いのですが、ゴテゴテしていることが多いので、今回はオレオレテンプレートを自分で管理・編集したいと思ったのでシェルスクリプトで作ってみました。

よかったら使ってみて下さい。
Github: https://github.com/ivgtr/tepy

## やりたいこと
- `template`ディレクトリを作り、その配下のディレクトリをテンプレートとして選択できるようにする
- 選択したテンプレートの中身を作業中のディレクトリにコピーする

## 実装
### 準備
`$HOME`ディレクトリにシェルスクリプト用のディレクトリを作り、その中に`template`ディレクトリを作成します。

```console:shell
$ mkdir "${HOME}/.sh" && mkdir "${HOME}/.sh/template"
```
この`template`ディレクトリ内に、使い回したいテンプレートをディレクトリごと入れていきます。

シェルスクリプト用のディレクトリ配下にシェルスクリプトを作り、書いていきます。

```console:shell
$ touch "${HOME}/.sh/main.sh"
```
### スクリプトを書いていく

```Bash:main.sh
function tepy () {
  # メイン処理
}
```
読み込み時に実行されてしまうのでスコープで囲う。
この関数名がコマンド名になるので入力しやすい物にしとく。
今回はTemplate-Copyなのでtepyと名付けました。

```Bash:main.sh
  template_path="${HOME}/.sh/template"
  if [ ! -d $template_path ]; then
    echo "Not found template directory."
    mkdir $template_path
    return
  fi
```
`if [ ! $hoge ]`でNOT条件、`if [ -d ${DIR} ]`で、ディレクトリの存在確認ができ、
エラーハンドリングとして、`template`ディレクトリが存在するかチェックし、存在しなかったら生成します。

```Bash:main.sh
  template_list=()
  for template in `find $template_path -maxdepth 1 -mindepth 1 -type d`; do
    template_list+=${template##*/}
  done
```
`hoge=()`でbash 配列を作ります。

テンプレートを列挙する為に、`find`で`"${HOME}/.sh/template"`内のディレクトリを探します。
オプションとして`-type d`を付けるとディレクトリのみが検索でき、`-maxdepth 1 -mindepth 1`で`template`本体を含まない一層目までの子ディレクトリを検索します。

取得したデータは`path`になっているので、template名としてディレクトリ名が欲しいので、`##*/`で左から最長の`/`を探し、削除。配列に追加します。

```Bash:main.sh
  if [ ${#template_list[@]} -gt 0 ]; then
    # メイン処理
  else
    echo "Not found tempate. pls add a directory."
    open $template_path
  fi
```
テンプレートが存在しない時はメッセージを出し、追加する様に`open`コマンドでディレクトリを表示します。
`${#array[@]}`で`$array`の`length`が取得でき、`if [ ${#array[@]} -gt 0 ]`で`${#array[@]}`が0より大きいか条件分岐しています。

```Bash:main.sh
  PS3="Please select a number: "
  select i in $template_list; do
    if [ -n "${i}" ]; then
      cp -i -r -v `find "${template_path}/${i}" -maxdepth 1 -mindepth 1` "$pwd"
      echo "Copying of ${i} completed."
    else
      echo "Cannot select ${REPLY}. Please select a number."
    fi
    break
  done
```
`PS3`変数を変更すると`select`などのプロンプトの表示が変更できるので、番号以外を入力されない様に変更。
`select i in $array`で簡易的な対話メニューが作れる。
入力を受け付けると`$i`にはテンプレート名が、`$REPLY`に入力した値が代入される。
エラーハンドリングとして`if [ -n $hoge ]`で`$hoge`の存在チェックをし、存在しない物を入力した際にはメッセージを出す。
存在した場合、`find`で指定したテンプレートのディレクトリ配下のファイルなどを、作業中のディレクトリにコピーする。

### コード全体

```Bash:main.sh
#!/bin/bash

function tepy () {
  template_path="${HOME}/.sh/template"
  template_list=()
  if [ ! -d $template_path ]; then
    echo "Not found template directory."
    mkdir $template_path
    return
  fi

  for template in `find $template_path -maxdepth 1 -mindepth 1 -type d`; do
    template_list+=${template##*/}
  done

  if [ ${#template_list[@]} -gt 0 ]; then
    PS3="Please select a number: "
    select i in $template_list; do
      if [ -n "${i}" ]; then
        cp -i -r -v `find "${template_path}/${i}" -maxdepth 1 -mindepth 1` "$pwd"
        echo "Copying of ${i} completed."
      else
        echo "Cannot select ${REPLY}. Please select a number."
      fi
      break
    done
  else
    echo "Not found template. pls add a directory."
    open $template_path
  fi

  return
}
```

### PATH を通す
`PATH`を通す為に、`.zshrc`もしくは`.bashrc`に`source "${HOME}/.sh/main.sh"`を記述し、保存したら、`source "${HOME}/.zshrc"`もしくは`source "${HOME}/.bashrc"`で再読み込みします。

## 実行
上記で関数名を命名した様に、`tepy`コマンドで呼び出せます。
`template`ディレクトリに適当にテンプレートにしたいディレクトリを作成し、`tepy`コマンドを打ちます。

例:

```console:shell
$ tepy
1) typescript  2) react
Please select a number: 1
```
やった！ぱぱぱっと環境構築ができました！
これでTypeScriptをもっと書き殴っていこうと思います。

## 終わりに
何か間違いがあればコメント等で指摘頂ければ幸いです。

## 参考
- https://eng-entrance.com/linux-shellscript-select