Linux基礎学習まとめ⑤ シェルスクリプトの構造(条件分岐、繰り返し、サブルーチン)

##概要
前回の記事では、シェルスクリプトの基本(順次実行)のみ触れました。
今回は、シェルスクリプトを構成す3つの重要な要素(条件分岐、繰り返し、サブルーチン)についてメモしておく。


##実行環境
- macOS BigSur 11.4
- CentOS7 (macOS上でLinux環境を構築する方法は[こちら](https://qiita.com/tamago3keran/items/781728815e359243ff33)を参考にしました)

##条件分岐
###1. if, else
if, else文を用いて条件分岐を行う。
基本形は以下の通り。

```Bash
# 書式

if [ 条件式1 ]; then  # 条件式を囲む左右の[]に一個ずつスペースが必要！
    # 条件1が成立する場合の処理
elif [ 条件式2 ]; then
    # 条件2が成立する場合の処理
else
    # どの条件にも合致しない場合の処理
fi # ifの逆で終わる
```
上記の書き方は
`if test 条件 ; then ... `の省略形。

比較演算子を用いて、様々な条件で値を比較したり、文字列を比較することもできる。
複数の条件を組み合わせることもできる。

例)

```Bash:if_else_example.sh
#!/bin/bash

# 変数xに入力されたデータを読み込む
read x 

# xの値によって条件分岐
if [ $x == 0 ]; then
    echo "x is zero."
elif [ $x == "A" ]; then
    echo 'x is "A".'
elif [ $x -ge 1 ] && [ $x -lt 10 ]; then
    echo '1 <= x < 10'
else
    echo 'None'
fi
```


また、値や文字列だけでなく、ファイルの種類なども条件として与えられる。

例）

```Bash:if_else_file_example.sh
#!/bin/bash

read name 

# name変数で指定されたものがファイルかディレクトリか、その他（シンボリックリンクなど）かどうかを条件分岐で確かめる
if [ -d ${name} ]; then # ディレクトリ
    echo "${name} is directory" 
elif [ -f ${name} ]; then # ファイル
    echo "${name} is file"
else # その他
    echo "${name} doesn't exit"
fi
```

if文の書き方の基本は[こちら](https://shellscript.sunone.me/if_and_test.html)、比較演算子などの例をめっちゃやってくれてるのが[こちら](https://qiita.com/toshihirock/items/461da0f60f975f6acb10)で、大変参考になりました。

###2. case
一対多の分岐を記述するための文。
指定した変数の値に対して、複数の条件分岐を書きやすい。

```Bash
# 書式

case 変数 in
    値 A)
        処理1;;
    値 B)
        処理2;;
    *) # どの条件にもマッチしなかった場合の処理は*)を用いる
        処理3;; 
esac # caseの逆で終わる
```

引数を比較に用いることで、テストファイルなども作成しやすい。

例）

```Bash:case_example.sh
#!/bin/bash

case $1 in 
    1)
        echo "Arguent is 1.";;
    A)
        echo "Argument is A.";;
    *)
        echo "Argument is not A or 1.";;
esac
```

##繰り返し
###1. for
100回など大きな回数で同じ処理を繰り返し行う際などに用いられる。

```Bash
# 書式
for 変数 in 値のリスト
   do
      # 処理
   done
```

例）

```Bash:for_example.sh
$ ls
test1 test2 test3 test4 test5

# コマンドラインからfor文を直接記述している
$ for i in `ls` # ``で囲むとコマンドの実行結果を値リストとして扱うことができる
> do
>   echo $i
> done
test1 # lsコマンドの実行結果が１行ごとに出力される
test2
test3
test4
test5
```

###2. while, until
`while`は条件が成立している間、`until`は条件が成立していない間ループを繰り返す。

```Bash
# 書式

while / until 条件式
do
    # 処理
done
```
C言語のように値をインクリメントしてループ処理をする場合は、`expr`コマンドを用いて計算を行う。
計算式や論理式を評価する`expr`コマンドの詳細は[こちら](https://www.atmarkit.co.jp/ait/articles/1712/28/news019.html)を参考にさせていただきました。

```Bash:while_sample.sh
$ count=1
$ while [ $count -lt 10] # countが10未満ならば処理を行う
> do
>  echo "Count is $count"
>  count=`expr $count + 1` # exprコマンドで値をインクリメントする
> done
Count is 1. # 1~9までが表示される
Count is 2.
Count is 3.
Count is 4.
Count is 5.
Count is 6.
Count is 7.
Count is 8.
Count is 9.
```

###3. select
値のリストに対し、インデックスを割り当て、入力されたインデックスの値に対応するデータに対して処理を行う。

```Bash
# 書式

select 変数 in 値のリスト
do
    # 処理
done

1) 値1 # 値のリストが表示される
2) 値2
3) 値3
...
$? n # 番号を入力
# nに対応する処理
$ # `Ctrl+C`で終了
```

例)

```Bash:select_example.sh
$ select name in "apple" "banana" "orange"
> do
>  echo "Name is $name"
> done
1) apple
2) banana
3) orange
＄? 2 # 2で"banana"を指定
Name is banana
$ 
```

###4. break, continue
`break`はループを抜け、`continue`はループの最初に戻る。

例）

```Bash:example.sh
while true
do
    echo "Continue? (y/n)"
    read input
    case $input in
         n) break # nが入力された場合、ループを抜ける
            ;;
         y) continue # yが入力された場合、ループの最初に戻る
            ;;
         *) echo "Please input y or n." # y, n以外が入力された場合、echo以降の文字列を出力してループを繰り返す
            ;;
    esac
done
```

実行結果

```
$ ./sample.sh
Continue? (y/n)
a
Please input y or n.
Continue? (y/n)
y
Continue? (y/n)
n
$ 
```

##サブルーチン(関数）
加算や配列の入れ替えなど、一連の処理をまとめて、再利用できるようにしたもの。
C言語とかやってるとすぐわかるはず。

```Bash
# 書式

関数名　()
{
　　　　# 処理
  return 変数名 # 戻り値
}
```

##番外編 : デバッグ
shコマンドに-xオプションをつけると、各行のコマンドや変数の中身を表示しながらスクリプトを実行する。

```
$ sh -x ./sample.sh
```


##終わりに
こういうプログラミングの基礎構造みたいなやつは、例を見ながら自分で、適当に条件や値を変えたり、繰り返しと条件分岐を組み合わせてみたりしながらやるのが一番わかりやすいと思います。

##参考文献
この記事は以下の情報を参考にして執筆しました。
- [Linux標準教科書(ver.3.0.2)](https://linuc.org/textbooks/linux/)