シェルとはユーザーとカーネルの間で、ユーザーの命令を行うソフトウェアのことです。シェルにも色々と種類がありますが、一番よく使われるのはbashで、LinuxやMacOSなどのUNIX系OSで使うことができます。以前、Batch : Sample programで説明しましたバッチと同じく、シェルもシェルスクリプトを作ることで、複雑な作業などの自動化が可能となります。

シェルは内容が膨大であるため、私が作成したサンプルプログラムを使い、基本的な使い方を紹介していきたいと思います。

Quiz

バッチでも説明しましたQuizプログラムをシェルで作ってみました。ロジックはほぼ同じですが、書き方は異なりますので、バッチのコードと比較しながらシェルの書き方を確認してみてください。バッチのポストと同様、コードを分割しながら説明します。

#!/bin/bash

# ログファイルの指定
declare -r log=quiz.log

# 集計変数の初期化
declare -i index=0
declare -i correct=0

先頭行の#!/bin/bashはshebangというインタプリタを指定する記述です。インタプリタはbashを使いますのでbashを指定しています。

declareは変数を宣言するコマンドです。declareを省略しても変数の宣言は出来ますが、declareを使うと変数にオプションを指定することが出来ます。例えば-rは読み取り専用で、-iは数値としての宣言を意味します。

while read line
do
  question[${index}]=`echo ${line} | awk -F',' '{print $1}'`
  answer[${index}]=`echo ${line} | awk -F',' '{print $2}'`
  index=${index}+1
done<input.txt
clear

変数の宣言の次はファイルの読み込み処理です。while read line do done<fileを使って簡単にファイルを1行ずつ読み込むことができます。ループの中では、questionanswerという配列変数に、それぞれファイルから読み込んだ問題と答えを設定し、配列のINDEXを表す変数indexもインクリメントしています。

バッチの時はforコマンドで問題と答えを分割していましたが、今回のシェルではechoawkを使って編集しています。echoはコンソール上に文字列などを表示するコマンドですが、変数に値を設定する時も使えます。 awkは文字列の編集などに使うコマンドですが、今回は下記の基本的な使い方だけを紹介します。

awk -F'区切り文字' '{awkコマンド}'

Quizサンプルでは、パイプ|より渡された1行の文字列を、標準出力を行うawkコマンドのprintを使って、,で区切った文字列をquestionanswerの配列変数に設定しています。

while :
do
  echo "Total stage count : "
  read stage
  clear
  if [ ${stage} -le ${index} ]; then
    break
  fi
done

問題と答えの読み込み後はreadコマンドを使って標準入力からの問題数を設定します。読み込んだ問題数よりも入力された数値が大きい場合はエラーになりますので、while :の無限ループの中で、読み込んだ問題数のindexと入力した問題数のstageを比較し、問題ない場合はループを抜け出します。

declare start=`date "+%H:%M:%S"`

for (( i=0 ; i < ${stage} ; i++ )) {
  while :
  do
    rand=`expr ${RANDOM} % ${index}`
    if [ -z ${rbox[${rand}]} ]; then
      rbox[${rand}]="pass"
      break
    fi
  done
  clear
  echo ${question[${rand}]}
  sleep 3

続いては問題の表示と答えの比較処理について説明します。コードが長いため、問題の表示と答えの比較処理を分けました。bashでも一般的なfor文の書き方が出来ます。但し、括弧は2回使います。forの中には更にwhile :を使って、ランダムに問題を表示しています。

rboxという配列を用意して、既に表示した問題は表示しないようにpassという文字列を設定しています。ifで使っている-znullを判断する演算子で、配列rboxに既にpassが入っている場合は、再度ランダムでINDEXを取得し、配列の中身を判断する仕組みです。

  while :
  do
  clear
    read input
    if [ "${input}" = "${answer[${rand}]}" ]; then
      correct=`expr ${correct}+1`
      break
    else
      echo ${answer[${rand}]}
      read wait
    fi
  done
}
clear

問題の表示が終わったら、再度while :を使って、標準入力で入力した答えが問題の答えと一致しているかを確認しています。もし答えが一致する場合、正解数であるcorrectがインクリメントされます。間違った場合は、read waitより、答えの入力を再度要求します。

echo ${start} >> ${log}
echo `date "+%H:%M:%S"` >> ${log}
echo "Total Stage : ${stage}" >> ${log}
echo "Total Correct : ${correct}" >> ${log}

exit 0

全ての問題の答え合わせが終わったら、ログファイルに開始時間と終了時間、問題数、正解数を追記します。コマンド結果の出力(リダイレクト)は>でも出来ますが、>>を使うと上書きされず、実行するたびに追加されます。最後はexitより0を返却してプログラムを正常終了します。

bashは一般的なプログラム言語と似たような書き方が多いため、説明を多く省いています。ループ処理や真偽判断、リダイレクト、特にawkなどは使い方が多いため、常に色々な使い方を試してみるのがいいと思います。