コマンドを1つずつ確認してもらいながら実行するシェルスクリプト

複数のコマンドを順に実行していくだけなら、シェルスクリプトとしてコマンドを羅列していけば良いが、

などの理由から、コマンド群が一気に実行されるのを避けたい場合がある。

そんな時、手っ取り早いのは「作っておいた手順書からコマンドを1行ずつコピペする」といったやり方が思いつくだろう。しかし、都度コピペするのは間違いが起こりやすかったりする。

そこで、スクリプトファイルにコマンドを羅列しておくのだが、実行は1行ずつ確認しながら行え、1コマンドごとのスキップや中止にも対応するためのシェルスクリプトを考えてみた。

コードは次のとおり。

#!/bin/bash

# 指定のコマンドを1つずつ、順に確認しながら実行する

# 実行したいコマンドを配列で定義する
cmds=(
  'echo Hello'
  'echo Hoge'
  'echo Fuga'
  'echo End'
)

# コマンドを順に確認しながら実行する
for cmd in "${cmds[@]}"; do
  # 未入力は許可しない
  while : ; do
    read -p "次を実行します。よろしいですか? [ $ ${cmd} ] : " confirm
    if [ -n "${confirm}" ]; then
      break
    fi
  done
  
  # 先頭1文字のみ取得する
  confirm="${confirm:0:1}"
  
  if [ "${confirm}" = 'y' -o "${confirm}" = 'Y' ]; then
    # y か Y なら eval で実行する
    eval "${cmd}"
  elif [ "${confirm}" = 's' -o "${confirm}" = 'S' ]; then
    # s か S ならこのコマンドをスキップして続行する
    echo 'スキップします'
  else
    # それ以外はスクリプトを中止する
    echo '中止します'
    exit 1
  fi
done

変数 cmds は配列になっている。ココに、順次実行していきたいコマンドを書いていく。

変数を使用する場合、コマンド内で使用するダブルクォートは \" とエスケープすれば良い。

# 適当な変数
new_branch_name=v2

cmds=(
  # 「git commit -m "Init : v2"」というコマンドに展開させたい場合
  "git commit -m \"Init : ${new_branch_name}\""
)

事前に設定するのはココだけで、for cmd in 以降の行はそのままで良い。

ココは何をしているかというと、変数 cmds から要素を1つずつ取り出し、read -p で実行確認した後、eval で実行している、というモノだ。

read -p 部分は何らかの文字を入力するまで同じ質問が繰り返されるようになっている (空白のまま Enter を押しても進まない)。そのコマンドを実行したい場合は「y」ないしは「Y」を、スキップしたい場合は「s」「S」を入力すれば良い。

次に Bash の変数展開を使って、入力された文字の先頭1文字のみを取得している。コレが "${confirm:0:1}" 部分。

あとは if 文で入力文字列を検証し、eval を実行するか、何もしないか、exit 1 で抜けるか、をハンドリングしている。この条件部分はお好みで、スキップを許可しないようにしたりしても良いだろう。その場合は elif のブロックを丸ごと削るだけで良い。

こんな感じで、指定のコマンドを1つずつ、ユーザに確認してもらいながら実行できるようになった。