shellの基礎構文

この記事は、CAM Advent Calendar 18日目の記事です
前回は @kkenya さんによる、[Chrome拡張機能で楽をしたい](https://qiita.com/kkenya/items/74aabaef5f4b6136ca90)でした。
記事一覧はこちらから↓
https://qiita.com/advent-calendar/2019/cam-inc
# 概要
shellに関して今までなんとなく使ってきましたが、基礎的な内容もよく知らないな〜と思ったので今回の記事にしました!
数あるshellの中でも今回は**bash**を採用します。
内容は初歩の初歩なのでshellを当たり前のように使っている方には何も面白くないかもしれませんので悪しからず。
記事に関しても初投稿になるので、typoや間違ってる事があればご指摘お願い致します。

# 変数の基本
自分の主観になりますが、shellは今まで触ってきた言語の中でも特徴が強く感じました。
カーネルとのインターフェースを担っているという性質上しかたないのかもしれませんが↓のように変数の宣言時にスペースを含めるとエラーとなってしまったり、宣言する時は変数名だけでなのですが使用する際には`$`を付けなければいけなかったり (一部の言語では変数に常に$をつけなければならない言語もありますが...) これが一番驚いたのですが型が**文字列型**しかないそうです。

``` .sh
# ↓変数宣言時にスペースを含めるとと駄目
a = 123
-bash: a: command not found # エラーになってしまう

# 変数を使う時は宣言した変数名の前に$をつけて使う
a=123
echo $a
123 # 123が出力される

# 変数は文字列の型しか存在しない
a=1
b=1
c=$a+$b
echo $c
1+1 # 変数が展開した状態の文字列が代入される
```
### 変数展開について
shellは様々な書き方により変数展開のが変わるという性質をもっています。
例えばこのような曖昧な変数名を使おうとすると変数abとみなされてしまうので期待した表示ができなくなります。

``` .sh
a=1
b=2
echo $ab

# 何も表示されない
```
そこで、前後の文字に影響を受けないようにするには{}でくくると変数名が明確になります。

``` .sh
a=1
b=2
echo ${a}${b}
12  # 期待した通りに表示される
```
{}この記法で文字列の一部を抜き出すという使い方もできます。

``` .sh
a=123
echo ${a:1:2}
23
```
#### クォートによる変数展開

shellではクォートの違いにより動作が変わるという特徴を持っています。
その違いによりどのような動作をするのか確認してみました。

``` .sh
a=date
echo '$a'
$a # 変数が展開されずにそのまま $a が表示される

echo "$a"
date # 変数が展開され date が文字列として表示される

echo `$a`
2019年 12月15日 月曜日 15時07分38秒 JST # 変数が展開されクォート内をコマンドとして実行される
```
ついでに、クォートの組み合わせで動作が変化するものもあるか検証してみました。

``` .sh
a=date
echo '"$a"'
"$a" # 展開されず
echo '`$a`'
`$a` # 同上

echo "'$a'"
'date' #  ''は無効になり中の変数が展開される
echo "`$a`"
2019年 12月15日 月曜日 15時15分38秒 JST # ""の中でも``は有効

echo `'$a'`
-bash: $a: command not found # $aがコマンドとして認識されるのでエラーとなる
echo `"$a"` 
2019年 12月15日 月曜日 15時15分38秒 JST # ``と動作は変わらない
```
↑の組み合わせで "``" が機能するので 「文字列+コマンド」 の組み合わせで色々できそうな気がしますね。

### 配列について
配列の扱いもやはり特殊ですね。

``` .sh
a=(1 2 3) # スペースで区切る
echo $a
1 # 最初の要素が表示される

echo ${a[0]}
1 # 指定したインデックスの要素を取り出す

echo ${a[@]} 
1 2 3 # 全ての要素を表示する

echo ${#a[@]}
3 # 要素数を表示する

a[0]=10 # 代入時にはカーリー・ブレイスは必要ない
echo ${a[@]} 
10 2 3

a+=(4 5) # 追加代入する
echo ${a[@]} 
10 2 3 4 5
```

# 制御構文
基本shellの制御構文は

``` .sh
if [ 条件式 ]
then
 処理
fi
```
このように`if`と`then`を行を分けて書く必要がありますが、可読性が落ちるので

``` .sh
if [ 条件式 ] ; then
 処理
fi
```
と書く場合が多いです。そして条件文の`[`ですが実はこれ構文の一部ではなくコマンドらしいです。
ですので`[ 1 = 1 ]`このようにスペースをしっかり取らないとエラーになってしまいます。
`[`コマンドが何をしているのか実際に使ってみます。

``` .sh
[ 1 = 1 ] # 何も戻ってこない...
```
特に何か起こる訳ではないのですが`$?`という特殊変数を使うことである程度何が起こっているのか分かるようになります。
(`$?`はシェルが最後に実行したコマンドの終了状態を保持している)

``` .sh
[ 1 = 1 ]
echo $?
0 # 成功したら0が保持されている

[ 0 = 1 ]
echo $?
1 # 失敗したときは1?
``` 
実は各種UNIX一般コマンドはコマンドの成否を戻り値という形で返しています。

- 0が成功
- 0以外が失敗

`[`がコマンド`if`がコマンドの終了状態で判断しているという事は、別に`[ 条件式 ]`のような形に拘る必要は無い訳です。
必要に応じて`grep`や`find`なので条件判断ができるということですね。

``` .sh
# 分かりやすくする為に出力を""で括ってます
touch test
if ls | grep test; then # grepの成功可否で判断できる!
 echo OK 
else
 echo NO
fi
"test" 
"OK"

if ls | grep test2; then
 echo OK 
else
 echo NO
fi
"NO"
```


# 最後に
よく分からないまま使っていたshellの記述に関して、実際にどういった動きをしているのかを確かめるプラス、
自分の備忘録として今回の記事を書きましたが、やはりshellは結構特殊な動作をするなーと改めて認識しました。
今後、業務の中でもshellを取り扱ったりshell scriptを書いたりすることが多くなるのでもっとshellについて詳しく学ぶべきだと再認識しました。

次は@camYoshimuraさんです。お楽しみに!
# 参考資料
今回の記事を書く上で参考にさせて頂きました。ありがとう御座います。
https://qiita.com/katsukii/items/383b241209fe96eae6e7
https://shellscript.sunone.me/if_and_test.html#if-%E6%96%87%E3%81%A8%E3%81%AF
https://qiita.com/a_yasui/items/ec4f75b300410af8958d