シェルスクリプトでターミナルの右上にわからなかった英単語を表示して復習する

## はじめに
エンジニアは英語から逃げることはできないと思います。
私はしばしば以下の気持ちに思い至る時があります。

- わからない英単語をWeｂで検索するのが面倒だな〜
- 調べた英単語を復習したいな〜（しない）

そこで！
ターミナルのコマンドでぱぱっと翻訳できて、
翻訳した英単語をいつでも復習できるようにターミナルをカスタマイズしました！

## 使用環境

- Mac OS ventura 13.1
- zsh 5.9 (x86_64-apple-darwin21.3.0)
- vistual studio codeのターミナル画面

## 成果物
![enja.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1650886/835e7e96-9bb7-73ef-8719-0ffe46a157fe.gif)
`enja`という自作コマンドを作成し、ターミナル内で翻訳が表示されます。
また、翻訳した英単語はターミナルの右上に表示され、コマンド実行の度にランダムで過去の履歴が表示されます。

## 事前準備

### 必要なツールインストールとファイル作成
```shell:.zshrc
# gshufコマンドをインストール
brew install coreutils
# transコマンドをインストール
brew install translate-shell
# 履歴ファイル作成
touch ~/.trans_history
```
### .zshrcで色を使えるようにする

```shell:.zshrc
# 色を使用出来るようにする
autoload -Uz colors
colors
```

## 英単語を登録する
`function enja()`を作成することで自作の`enjaコマンド`が実行できます。
引数に英単語を入力することでターミナルには翻訳の結果が表示され、履歴をファイルに保存しています。


```shell:.zshrc
## 英語から日本語へ翻訳
function enja()
{
  if [ $# = 0 ]; then
    echo "引数に英語を入力してください。"
    return 1
  fi

  if [ $# != 1 ]; then
    local error_text='引数には１つの英語を入力してください。文章は「"」で囲んでください。'
    echo $error_text
    return 1
  fi
 # 結果を表示
 trans en:ja $1
 # 簡易結果をファイルに保存(英語 日本語 日本語の文字数)
 # 例）test テスト 3
 local ja=$(trans en:ja -b $1)
 local count=$(echo -n $ja | wc -m | sed 's/ //g')
 echo "$1 $ja $count" >> ~/.trans_history
}
```

### シェル独特の表記

| 変数名 | 説明 | 
|:-----------|:------------|
| $#   | 位置パラメータ（引数）の個数 |
| $(コマンド)    | $()内に書かれたコマンドを実行     | 
| local 変数名       | 関数のスコープ内で使える変数の宣言        
| return 数値 |関数の処理を終了する。一般的に正常終了が0、エラーを1で返す　|

### transコマンドの使い方

| コマンド | 説明 | 
|:-----------|:------------|
| `trans en:ja 英語` | 英語から日本語に翻訳した結果を表示 |
| `trans en:ja -b 英語`|簡略された翻訳を表示 | 

ちなみに`shellllllllll`のような存在しない単語も頑張って翻訳してくれます。
「シェルルルルル」と翻訳してくれました。

### 実行結果の保存で日本語訳の文字数を入れている理由

履歴をターミナルの右上に表示するため、ターミナル全体から文字数を計算しています。
`RPROMPT`はターミナルの右に文字を表示できる機能ですが、右上ではなく右下に表示してしまうため採用していません。

### コマンド実行

```shell:ターミナル画面
# enja 翻訳したい英単語
enja test
```

## 英単語と日本語訳を表示する

```shell:.zshrc
# PROMPTテーマ
setopt prompt_subst #プロンプト表示する度に変数を展開

# プロンプトの表示直前に毎回実行される
precmd () {
  # プロンプト右上表示内容
  local LEFT="%F{cyan}%~%f"
  # 右側の表示文字数を計算
  # カレントパス=絶対パス-ホームパス
  local HOME_PATH=$(echo $HOME)
  local ABSOLUTE_PATH=$(pwd)
  local SPACE_COUNT=3
  # カレントパス文字数-ホームパス文字数+スペース数
  local LEFT_WIDTH=$((${#ABSOLUTE_PATH}-${#HOME_PATH}+$SPACE_COUNT))

  # プロンプト左上表示内容
  local TRANS_RANDAM=$(gshuf ~/.trans_history -n 1)
  local JA_COUNT=0
  local RIGHT=""

  if [ ${#TRANS_RANDAM} != 0 ]; then
    local JA_COUNT=$(echo -n $TRANS_RANDAM | awk '{ print $3 }')
    local RIGHT=$(echo -n $TRANS_RANDAM | awk '{ print $1 " " $2 }')
  fi

  # プロンプトの幅計算（=全体の幅-左-右-日本語）
  # 全角の分のスペースが必要なので日本語の文字数を全体から引いている
  local PADDING_WIDTH=$(($COLUMNS-$LEFT_WIDTH-${#RIGHT}-$JA_COUNT))

  # $LEFT と $RIGHT の間をスペースで埋めて表示
  local RIGHT_COLOR="%F{black}$RIGHT%f"
  print -P $LEFT${(r:$PADDING_WIDTH:: :)}$RIGHT_COLOR
}
PROMPT='%f$ '
```

### ファイルからランダムに一行取得する

```shell
local TRANS_RANDAM=$(gshuf ~/.trans_history -n 1)
```

`-n 行数`オプションは表示する行数を指定しています。


## 全体（.zshrc）

```shell:.zshrc
# 色を使用出来るようにする
autoload -Uz colors
colors

################################################
# PROMPTテーマ
setopt prompt_subst #プロンプト表示する度に変数を展開

# プロンプトの表示直前に毎回実行される
precmd () {
  # プロンプト右上表示内容
  local LEFT="%F{cyan}%~%f"
  # 右側の表示文字数を計算
  # カレントパス=絶対パス-ホームパス
  local HOME_PATH=$(echo $HOME)
  local ABSOLUTE_PATH=$(pwd)
  local SPACE_COUNT=3
  # カレントパス文字数-ホームパス文字数+スペース数
  local LEFT_WIDTH=$((${#ABSOLUTE_PATH}-${#HOME_PATH}+$SPACE_COUNT))

  # プロンプト左上表示内容
  local TRANS_RANDAM=$(gshuf ~/.trans_history -n 1)
  local JA_COUNT=0
  local RIGHT=""

  if [ ${#TRANS_RANDAM} != 0 ]; then
    local JA_COUNT=$(echo -n $TRANS_RANDAM | awk '{ print $3 }')
    local RIGHT=$(echo -n $TRANS_RANDAM | awk '{ print $1 " " $2 }')
  fi

  # プロンプトの幅計算（=全体の幅-左-右-日本語）
  # 全角の分のスペースが必要なので日本語の文字数を全体から引いている
  local PADDING_WIDTH=$(($COLUMNS-$LEFT_WIDTH-${#RIGHT}-$JA_COUNT))

  # $LEFT と $RIGHT の間をスペースで埋めて表示
  local RIGHT_COLOR="%F{black}$RIGHT%f"
  print -P $LEFT${(r:$PADDING_WIDTH:: :)}$RIGHT_COLOR
}
PROMPT='%f$ '

################################################################
# 関数
## 英語から日本語へ翻訳
function enja()
{
  if [ $# = 0 ]; then
    echo "引数に英語を入力してください。"
    return 1
  fi

  if [ $# != 1 ]; then
    local error_text='引数には１つの英語を入力してください。文章は「"」で囲んでください。'
    echo $error_text
    return 1
  fi
 # 結果を表示
 trans en:ja $1
 # 簡易結果をファイルに保存(英語 日本語 日本語の文字数)
 # 例）test テスト 3
 local ja=$(trans en:ja -b $1)
 local count=$(echo -n $ja | wc -m | sed 's/ //g')
 echo "$1 $ja $count" >> ~/.trans_history
}
```

## おわりに

シェルはプログラミングとはまた違った記述の仕方で奥が深いなと思いました。
（パイプラインとか実行結果を変数にいれたりとか。。。）
なんとなく、パズルを解いている感覚でした笑

コード量が長くなってしまいましたが、やりたかった動作ができて満足です！

## 参考
https://ryota2357.com/blog/2022/zsh-prompt-custom/

https://rn4ru.com/blog/posts/shuffling-with-gshuf/

https://www.aizulab.com/blog/translate-shell/

https://www.amazon.co.jp/%E6%96%B0%E3%81%97%E3%81%84%E3%82%B7%E3%82%A7%E3%83%AB%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8-%E4%B8%89%E5%AE%85-%E8%8B%B1%E6%98%8E/dp/4797393106