サーバーエンジニアの研修に使える！シェルスクリプト問題集

# はじめに
サーバーエンジニアの研修用に使ったシェルスクリプトの問題集です。
Linuxサーバーを使って、ちょっとしたシェルスクリプトを書きたい方におすすめです。

# 算術演算について

シェルスクリプトでは算術演算にexprコマンドを使います。

```
a=`expr 1 + 2`
```

またbashの機能として

```
a=$(( 1 + 2 ))
```

のように $(()) で囲われた部分を算術演算することもできます。
今回はexprコマンドを使用します。


# 問１

変数を3つ使用し、四則演算の減算を行うスクリプトを作成せよ。
変数は a=10 、 b=5 、cには a-b の結果を格納すること。

### 回答例
```bash:1.sh
#!/bin/bash

a=10
b=5

c=`expr $a - $b`
echo $c
```

### 実行結果
```
$ ./1.sh
5
```

### 解説
変数に値を代入し、exprコマンドを使って計算した結果をcの中に格納して出力します。
変数名の先頭に$(ダラー)を付けることで変数に格納している値を使用することができます。


# 問２

変数を使用し、キーボードから入力した文字列を表示させるスクリプトを作成せよ。
入力した文字を格納する変数aを使用すること

### 回答例
```bash:2.sh
#!/bin/bash

echo "入力"
read a

echo "出力"
echo $a
```

### 実行結果
```bash
$ ./2.sh
入力
あいうえお
出力
あいうえお
```

### 解説
readコマンドは標準入力からデータを読み込みます。ここでは読み込んだデータをaに格納し、echoコマンドを使って出力します。


# 問３

現在の日時と時間を表示するスクリプトを作成せよ。
日、月、年、時間の順で表示させること。
日、月、年、時間の間には、「、」を表示させること。

### 回答例
```bash:3.sh
#!/bin/bash

date "+%d日、%m月、%Y年、時刻%T"
```

### 実行結果
```bash
$ ./3.sh
26日、07月、2011年、時刻11:18:48
```

### 解説
時刻を表示するにはdateコマンドを使います。dateコマンドに+から始まる引数を渡すことで指定したフォーマットで出力することができます。
%を使って指定したフィールドの値を取得することができます。

```
%d	月内通算日数 (01..31)
%m	月 (01..12)
%Y	年 (1970...)
%T	時刻、24 時間 (hh:mm:ss)
```

dateコマンドの詳しい使い方はman dateコマンドで見ることができます。


# 問４
if文を使用し、実行時点の「午前、午後」の判別を行うスクリプトを作成せよ。

### 回答例
```bash:4.sh
#!/bin/bash

hour=`date "+%H"`

# 12より小さい時の処理
if [ $hour -lt "12" ]; then
  echo "午前です"
# 12以上の時の処理
elif [ $hour -ge "12" ]; then
  echo "午後です"
fi
```

### 実行結果
```bash
$ date
2011年  7月 25日 月曜日 11:00:19 JST
$ ./4.sh
午前です
```

### 解説
dateコマンドの引数で ”+%H” を渡します。

```
%H　時 (00..23)
```

変数hourには現在の時刻00～23が入ります。条件式でhourが12より小さければ「午前です」、hourが12以上であれば「午後です」と出力します。


# 問５
for文を使用し、１週間の間で、何日忙しいか表示させるスクリプトを作成せよ。
月曜日から日曜日にかけてyes / no で入力できるようにすること。

### 回答例
```bash:5.sh
#!/bin/bash

# 忙しいかった日をカウントする変数
busyday=0

for week in 月 火 水 木 金 土 日; do

  echo "$week曜日は忙しかったですか？(yes/no)"
  read busy

  case $busy in
    yes) busyday=`expr $busyday + 1`;;
    no) continue;;
  esac
done

echo "$busyday日、忙しい日がありました。"
```

### 実行結果
```bash
$ ./5.sh
月曜日は忙しかったですか？(yes/no)
yes
火曜日は忙しかったですか？(yes/no)
no
水曜日は忙しかったですか？(yes/no)
yes
木曜日は忙しかったですか？(yes/no)
no
金曜日は忙しかったですか？(yes/no)
no
土曜日は忙しかったですか？(yes/no)
no
日曜日は忙しかったですか？(yes/no)
yes
3日、忙しい日がありました。
```

### 解説
まずカウント用変数busydayを用意します。次にfor文のリストに月～日曜日までの7つの値を用意します。for文はリストの値をweek変数に格納してリストが終わるまで実行します。

readコマンドでyes / noの入力をbusy変数に格納します。

次にcase文を使ってbusy変数がyesの場合はbusydayをインクリメントして、noの場合はcontinueで処理を続けます。


# 問６
if文を使用し、入力された値を100未満、100以上1000未満、1000以上かを判断するスクリプトを作成せよ。
「100未満です」、「100以上1000未満です」、「1000以上です」のいずれかを返すこと。

### 回答例
```bash:6.sh
#!/bin/bash

echo "数値を入力"
read num

if [ $num -lt "100" ]; then
  echo "100未満です"
elif [ $num -ge "100" ] && [ $num -lt "1000" ]; then
  echo "100以上1000未満です"
elif [ $num -ge "1000" ]; then
  echo "1000以上です"
fi
```

### 実行結果
```
$ ./6.sh
数値を入力
99
100未満です

$ ./6.sh
数値を入力
100
100以上1000未満です

$ ./6.sh
数値を入力
999
100以上1000未満です

$ ./6.sh
数値を入力
1000
1000以上です
```

### 解説
変数numに入力された値が条件式に当てはまるものを出力します。

```bash
# numが100以下で未満であれば実行される
if [ $num -lt "100" ];
```
```bash
# numが100以上 かつ numが1000未満であれば実行される
elif [ $num -ge "100" ] && [ $num -lt "1000" ];
```

```bash
# numが1000以上であれば実行される
elif [ $num -ge "1000" ];
```


# 問７
while文を使用し、入力された値をデクリメントしながら表示し、5になった時点で終了するスクリプトを作成せよ。「5」未満の入力はエラーメッセージを表示すること。

### 回答例
```bash:7.sh
#!/bin/bash

echo "5以上の数値を入力してください"
read num

if [ $num -ge "5" ]; then
  echo "出力"
  while [ $num -ge "5" ]; do
    echo "$num"
    num=`expr $num - 1`
  done
  echo "終わり"
else
  echo "5以上の数値ではありません。"
fi
```

### 実行結果
```
$ ./7.sh
5以上の数値を入力してください
4
5以上の数値ではありません。

$ ./7.sh
5以上の数値を入力してください
15
出力
15
14
13
12
11
10
9
8
7
6
5
終わり
```

### 解説

変数numに格納された値が5以上であれば処理を実行します。
while文でnumの値が5より小さくなるまでnumの値を出力しながらデクリメントしていきます。
numが5より小さくなれば処理を終了します。


# 問８
until文を使用し、5から1まで順に表示していくスクリプトを作成せよ。

### 回答例
```bash:8.sh
#!/bin/bash

count=5
until [ $count -eq "0" ]; do
  echo $count
  count=`expr $count - 1`
done
```
### 実行結果
```
$ ./8.sh
5
4
3
2
1
```

### 解説
まず変数countに5を代入します。until文では条件が成立しない間ループを繰り返します。なのでcount変数が0になるまでcount変数を出力しながらデクリメントしていきます。
countが0になれば条件が成立するのでループを終了します。

# 問９
変数numを乱数で宣言し、入力した値が同じになるまで繰り返すスクリプトを作成せよ。
while文を使用すること。
入力値が乱数より大きいか小さいかを表示させること

### 回答例
```bash:9.sh
#!/bin/bash

num=`expr $RANDOM % 10`
echo "数当てゲーム"

while :
do
  echo "0～9の数値を入力してください"
  read inputNumber

  if [ $inputNumber -eq $num ]; then
    echo "正解！$numでした"
    break
  elif [ $inputNumber -gt $num ]; then
    echo "もっと小さい"
  elif [ $inputNumber -lt $num ]; then
    echo "もっと大きい"
  fi
done
```

### 実行結果
```
$ ./9.sh
数当てゲーム
0～9の数値を入力してください
4
もっと大きい
0～9の数値を入力してください
7
もっと小さい
0～9の数値を入力してください
6
もっと小さい
0～9の数値を入力してください
5
正解！5でした
```

### 解説
bashではRANDOMという乱数を生成する変数が用意されています。RANDOMは0から32767のランダムな値を生成します。変数numにはRANDOMで取得した値を10で割った余りが格納されます。なのでnumには0～9までの値が格納されます。

この例では正解するまでwhileで処理を繰り返し、正解するとbreakでループを抜けます。
whileで無限にループさせたいときには

```bash
while :
do
done

while [ 1 ]
do
done
```

などと書きます。

変数inputNumber に0から9までの入力された値を読み込みます。

```bash
# inputNumber とnumが一致した場合
if [ $ inputNumber -eq $num ];
```
inputNumber とnumが一致した場合は”正解！$numでした”と出力した後、breakしてループを抜けます。

```bash
# inputNumber がnumより大きい場合
elif [ $ inputNumber -gt $num ];
```
inputNumber がnumより大きい場合は”もっと小さい”と出力します。

```bash
# inputNumber がnumより小さい場合
elif [ $ inputNumber -lt $num ];
```
inputNumber がnumより小さい場合は”もっと大きい”と出力します。


# 問１０
四則演算をメニュー形式で行えるスクリプトを作成せよ。

### 回答例
```bash:10.sh
#!/bin/bash

while :
do
  echo "四則演算 1)加算 2)減算 3)乗算 4)除算 5)終了"
  read calc

  case $calc in
    1)
      echo "加算を始めます"
      echo "最初の値を入力"
      read num1
      echo "次の値を入力"
      read num2
      answer=`expr $num1 + $num2`
      echo "$num1 + $num2 = $answer です"
    ;;
    2)
      echo "減算を始めます"
      echo "最初の値を入力"
      read num1
      echo "次の値を入力"
      read num2
      answer=`expr $num1 - $num2`
      echo "$num1 - $num2 = $answer です"
    ;;
    3)
      echo "乗算を始めます"
      echo "最初の値を入力"
      read num1
      echo "次の値を入力"
      read num2
      answer=`expr $num1 '*' $num2`
      echo "$num1 x $num2 = $answer です"
    ;;
    4)
      echo "除算を始めます"
      echo "最初の値を入力"
      read num1
      echo "次の値を入力"
      read num2
      answer=`expr $num1 '/' $num2`
      echo "$num1 / $num2 = $answer です"
    ;;
    5) break;;
  esac
done

echo "終了しました"
```

### 実行結果
```
$ ./11.sh
四則演算 1)加算 2)減算 3)乗算 4)除算 5)終了
1
加算を始めます
最初の値を入力
3
次の値を入力
4
3 + 4 = 7 です
四則演算 1)加算 2)減算 3)乗算 4)除算 5)終了
2
減算を始めます
最初の値を入力
10
次の値を入力
3
10 - 3 = 7 です
四則演算 1)加算 2)減算 3)乗算 4)除算 5)終了
3
乗算を始めます
最初の値を入力
5
次の値を入力
5
5 x 5 = 25 です
四則演算 1)加算 2)減算 3)乗算 4)除算 5)終了
4
除算を始めます
最初の値を入力
10
次の値を入力
2
10 / 2 = 5 です
四則演算 1)加算 2)減算 3)乗算 4)除算 5)終了
5
終了しました
```

### 解説
メニュー形式で計算方法を選べるようにします。calc変数に入力された値を条件に処理をcase文によって分けています。
計算方法としてはreadコマンドにてnum1、num2の値を読み込み、exprコマンドにて計算した結果をanswer変数に代入し出力しています。
注意しなければならないのは乗算と、除算を意味する*（乗算） / （除算）は特殊文字として扱われるのでシングルクォートで囲って文字をエスケープしなければなりません。

```
乗算　answer=`expr $num1 '*' $num2`
除算　answer=`expr $num1 '/' $num2`
```

この実行例ではメニューで5が選択されるまでwhile文にて処理を繰り返します。

以上で終わります。