テキストファイルに記載されたコマンドを半自動で実行するTeraTermマクロ

# はじめに
手順書のコマンドをサーバ上で実行する作業を支援するために、テキストファイルに記載したコマンドを1行ずつユーザの確認を取りながら実行するTera Termマクロを紹介します。

![ask_exec_command.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1423540/aa677ea8-b6f6-3e01-5196-73077975d9e1.png)

- フォルダに置いた複数のテキスト形式のコマンドリストファイルから利用するファイルを選択することができます。
- コマンドの開始位置を指定でき、作業を中断後に途中から実行することができます。
- コマンドを記載したテキストファイルを用意すればよいので、本格的なTera Termマクロを作るほどではない用途に便利です。
- 環境
  - Tera Term: Version 4.106

# 使い方
### 0. コマンドリストファイル準備
マクロを配置しているディレクトリに`command_list`というディレクトリを作成し、その中に実行するコマンドを記載した以下のようなテキストファイル形式のコマンドリストファイルを用意します。

```txt:command_list.txt
# コメント行
date
pwd

# /tmp配下に移動してファイル一覧を表示
cd /tmp
ls
```

- 行頭の1文字目が、`#`から始まる行はコメント行として扱います
- 空行は無視されます
- コマンドリストファイルの文字コードは、SJISにします（`filereadln`関数がUTF-8に対応していないという情報があるため）
  - 参考: http://ttm.jimba.ddo.jp/adiary.cgi/teraterm/index/macro/spec/cmd/filereadln

### 1. マクロ実行/コマンドリストファイル選択
![select_list_file.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1423540/55509601-0f9c-af20-b655-cdc51707d488.png)


- Tera Termのメニューから「コントロール」＞「マクロ」を選び、マクロを実行します。
- 実行すると、コマンドリストファイルの一覧が表示されるので、利用するファイルを選択します。

### 2. 開始コマンド指定
![select_start_command.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1423540/94f0bef4-928f-77be-7796-b2363cd3fd31.png)

- 実行するコマンドの位置を指定するリストボックスが表示されます。
- 途中から実行する場合は実行するコマンドを選びます。

### 3. 1行ずつコマンドを実行
![ask_exec_command.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1423540/aa677ea8-b6f6-3e01-5196-73077975d9e1.png)

- 実行するコマンドが入力された状態で、ユーザに実行するかを確認するダイアログが出現します。
  - 「はい」を押すとエンターキーを押下したことになり、コマンドが実行されます。「いいえ」を押すと実行を中止します
  - 実行するコマンドを修正したい場合は、入力されたコマンドを直接修正したあとに「はい」を押します。

### 4. コマンド実行結果確認
![check_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1423540/6450d749-7a82-9549-2ac1-581090904f48.png)


- 実行後は確認を促すダイアログが表示されるので、出力を確認して、「OK」を押します。「OK」を押すと次のコマンドが入力されます。
- 最後のコマンドまで実行が完了すると、「完了しました」が表示されてマクロが終了します。

:::note warn
注意
ログは本マクロでは取得していないので、必要な場合は手動で取得してください。
:::

# マクロコード
作成したマクロコードを掲載します。
## コード全体
```tex:command_executer.ttl
; コマンドリストファイルが格納されたディレクトリ名
CMD_LIST_DIR = 'command_list'

; コマンドの実行終了を検知するためのプロンプトの表示の指定
; 「]#」または「]$」を含むことを想定
PROMPT_REGEX = '\][#$]'

; コマンドリストファイル・実行コマンドを格納する配列要素数の最大値
ARRAY_MAX_SIZE = 128

; マクロの存在するディレクトリを取得
getdir DIR

; コマンドリストのあるディレクトリのパス
sprintf2 CMD_LIST_DIR_PATH '%s\%s' DIR CMD_LIST_DIR

; コマンドリストファイルの一覧を格納する配列
strdim CMD_FILE_LIST ARRAY_MAX_SIZE
; 実行コマンドを格納する配列
strdim CMD_LIST ARRAY_MAX_SIZE

setdir CMD_LIST_DIR_PATH

; コマンドリストファイル(.txt)の一覧を取得
extpat = '*.txt'
findfirst dh extpat filename

i = 0
while result
  if i >= ARRAY_MAX_SIZE then
    messagebox 'コマンドリストファイルが多すぎます' 'エラー'
    findclose dh
    end
  endif

  CMD_FILE_LIST[i] = filename
  findnext dh filename
  i = i + 1
endwhile

findclose dh

listbox '--------選択してください--------' 'コマンドリストファイル選択' CMD_FILE_LIST
n = result

if n == -1 then
  messagebox 'キャンセルしました' 'キャンセル'
  end
endif

sprintf2 command_list_file '%s\%s' CMD_LIST_DIR_PATH CMD_FILE_LIST[n]

fileopen file_handler command_list_file 0
if file_handler == -1 then
  messagebox 'ファイルを開けません' 'エラー'
  end
endif

; 選択されたコマンドリストファイルの中身を読み取っていく
command_length = 0
while 1
  filereadln file_handler buf
  ; ファイルを最後まで読み取ったらループを抜ける
  if result then
    break
  endif
  ; 空行は読み飛ばす
  strcompare buf ''
  if result == 0 then
    continue
  endif
  ; 行頭が「#」の行はコメントとして扱う
  strmatch buf '^#'
  if result then
    continue
  endif

  if command_length >= ARRAY_MAX_SIZE then
    messagebox '実行コマンド数が多すぎます' 'エラー'
    fileclose file_handler
    end
  endif

  CMD_LIST[command_length] = buf

  command_length = command_length + 1
endwhile

fileclose file_handler

; コマンドの開始位置を指定させる
listbox '--------開始コマンド位置選択--------' '開始位置選択' CMD_LIST
offset = result

if offset == -1 then
  messagebox 'キャンセルしました' 'キャンセル'
  end
elseif offset >= command_length then
  messagebox '指定が不正です' 'エラー'
  end
endif

; 念のため受信バッファをクリア
flushrecv

; コマンドを1行ずつ実行していく
for k offset command_length - 1
  send CMD_LIST[k]
  mpause 200
  yesnobox '実行しますか？' '実行確認'
  if result == 0 then
    messagebox '中止しました' '中止'
    end
  endif
  sendln
  waitregex PROMPT_REGEX
  messagebox 'コマンドが実行されたことを確認してください' '結果確認'
  mpause 200
next

messagebox 'マクロが完了しました' '完了'

exit
```

## コード説明
コードの一部について説明します。

### コマンドリストファイル一覧の読み取り
```
; コマンドリストファイル(.txt)の一覧を取得
extpat = '*.txt'
findfirst dh extpat filename

i = 0
while result
  if i >= ARRAY_MAX_SIZE then
    messagebox 'コマンドリストファイルが多すぎます' 'エラー'
    findclose dh
    end
  endif

  CMD_FILE_LIST[i] = filename
  findnext dh filename
  i = i + 1
endwhile

findclose dh
```

- ディレクトリ内にあるテキストファイル(`.txt`の名前のファイル)を探して、ファイル名を配列に格納していきます。
- 以下のリファレンスを参考にして、`findfirst`、`findnext`、`findclose`を使って実現しています。

https://ttssh2.osdn.jp/manual/4/ja/macro/command/findoperations.html

### コマンド実行完了の待機

```
PROMPT_REGEX = '\][#$]'
～(省略)～
waitregex PROMPT_REGEX
```

- コマンドの実行完了を検出するために、`[opc@vm-dev1 tmp]$`のような`]#`または`]$`という文字列を含むプロンプトの表示を待ちます。
- 作業対象マシンのプロンプトの表示が異なる場合は、`PROMPT_REGEX`を変更します。

https://ttssh2.osdn.jp/manual/4/ja/macro/command/waitregex.html

# 参考
マクロの作成、記事の執筆にあたり以下のサイトを参考にさせていただきました。

https://qiita.com/kawasima/items/3dfba8b7663a8e425520

http://ttm.jimba.ddo.jp/adiary.cgi/teraterm/

https://qiita.com/malbare932/items/831150a72fe7dba63cb2