僕は雰囲気でシェルを使っている

## 背景

- ラズパイやりたい
- よくエラーが出る。出た場合に、スマートに原因確認したい
  - 人口少ないのでググっても解決されないこと多々ある
- シェル読めばいいのでは？
- 雰囲気でしか読めない
- シェルの知識を一回洗いたい
- ということで改めて雰囲気でやってたところお勉強

## 注意
筆者はターミナルでzsh使ってますが、クウォーティングがbashと異なるせいか、ターミナルで思ったように動かないことが多発しました。そんなときはきちんとファイルにして、shebangで`/bin/bash`を設定して遊んで見ましょう。zshなんか個人の問題だしシェルスクリプトをzshで書くのはやめようね。[色々困る](https://qiita.com/umasoya/items/826d757a7d4353a1e259)ので

### そもそも入力されたコマンドってどうなってるの？
雰囲気というか、全く勉強したことなかった笑
どうやらLinuxでは以下の順番でシステムコールというものが実行されています、

- fork = プロセスをコピーして子プロセスを作成する
- exec = コマンドを実行する
- wait = 親プロセスが子プロセスを監視する
- exit = 実行結果を返す

1. forkによって現在のコマンドラインのプロセス(以降,X)から子プロセス(以降,x)をコピーして作成
2. Xはwaitによって、子プロセスを監視
3. x上でexecによってコマンドが実行され、exitで結果が返される
4. waitが感知してコマンドを終了する

###  shebang
シェルスクリプト先頭にあるこういう表現。

```
#!/bin/bash
```

```
#!/bin/sh
```

ただのコメントやん。と思ってしまいますが、実はコメントではないので、なんでも書いていいわけではないようです。

実は、先ほど書いた`exec`システムコールに関係します
execシステムコールは、**バイナリ**形式のファイルを読み込む仕組みです
しかし、**ファイルの先頭が`#!`となっている場合のみ、`#!`から改行までをコマンド、それ以降は、そのコマンドにファイルが引数として渡されているとして扱われます。**

なので、例えば

```sample_php.sh
#!/usr/bin/env php
<?php  
    echo "Hello,world";  
?>
```
これを、こうすると動くんです

```
$ ./sample_php.sh
```

便利やんと思う反面、shebangを扱う上で弱点があるようです。
ファイルに必ず実行権限がないと実行できないようです

```sh
$ chmod -x sample.sh
$ ./sample.sh
zsh: permission denied: ./sample.sh
$ chmod +x sample.sh
$ ./sample.sh
Hello,world
```

この問題は、直接shebangをコマンドラインで指定すると解決されます。

```sh
$ chmod -x sample.sh
$ /bin/bash sample.sh
Hello,world
```

環境構築しているときに、たまにこういう場面きますよね、こういう理由だったみたいです。

### sourceコマンド
よく、`.bashrc`を修正した後、その修正を反映させるには`source ~/.bashrc`をしろっていうのをみますね、
これに使われる`source`コマンドですが実はスクリプトファイルを1行ずつ読んでいっているだけなんです。これを聞くと普通のshと同じようです。唯一違う点は、sourceは子プロセスで実行しないんです。なので変数などの設定が現在のプロセスに全て引き継がれます。だから追加したばかりのaliasが使えたりするんですね、

### ブレース展開
パス名展開(`*.sh`とか`sample.[ch]`とかのパス名を展開する機能)はみなさん知っていると思いますが、ブレース展開というものもあります

めっちゃ便利そうです。

```sh
mkdir {src,test} // srcディレクトリ,testディレクトリを作成
mkdir dir_{1..100}  //ディレクトリを`dir_1`から`dir_100`まで作成
mkdir sample-{a..d} // sample-aからsample-dまで作成
mkdir sample-{4..11..2} //4から2ずつ増えて11まで展開する。従って `4,6,8,10`として展開され作成
```
やばい、めっちゃ便利。

### パラメータ展開

```sh
${変数:-値}
```
たまにみますよねこの表現、`変数`に値が設定されていない場合は`値`を使うみたいです。

コマンドの引数が設定されていない場合のデフォルト値を設定する際によく使われてます

```myecho.sh
#!/bin/bash
echo ${1:-デフォルトパラメータだよ}
```

```
$ ./myecho.sh
デフォルトパラメータだよ
$ ./myecho.sh 引数１
引数１
```

これと似た表現で、以下のようなものもありますね、

```sh
${変数:=値}
```

これはどうやら`変数`が空文字または未定義だった場合に`値`を代入して**展開します**

さらに似たような表現で

```sh
${変数:?値}
```
もちょくちょく見ます。

これは、`変数`が未定義だった場合は標準エラーに`値`を出力します。

```sh
$ echo ${file:?Please set file}
zsh: file: Please set file
```

使いどころありすぎてイイ！

### if文
以下のように`[]`があったりなかったりと3パターン存在しているのがすごい気になってた。

```sh
if コマンド; then
    :
fi

if [条件っぽいもの]; then
    :
fi

if [[条件っぽいもの]]; then
    :
fi
```

ちなみに、`:`はヌルコマンドといって、文字どおり何も実行しない。しかしこれを書かないとなんか処理しろやゴルァと言われるので書いている


#### まず一つ目`if コマンド; then ...`

コマンドの実行結果は,正常終了なら0,それ以外なら0以外を返す。
`if {コマンド}; then ....`の場合は、そのコマンドの実行結果が0である場合に条件に合致するということだ

```sh
if cd ~/sample; then
    echo 'Dir exists.'
else
    echo 'Not exists.' >&2
fi
```

この場合、`cd ~/sample`で、sampleディレクトリが存在している場合は条件内に入る。

`echo 'Not exists.' >&2`は、標準出力を標準エラー出力ヘリダイレクトしている。エラーを手っ取り早く表示するにはこの方法がいいらしい

#### 次に`if [条件っぽいもの]; then ...`
独特の記法がされている。これは**test**コマンドのエイリアスらしい。

testコマンドとは、引数に独自の演算子を渡すと、文字列のチェックやファイルの権限確認などを行なってくれる

|演算子|0(true)を返す場合|
|:--:|:--:|
|-a x|xが存在する場合|
|-e x|xが存在する場合|
|-d x|xがディレクトリの場合|
|-f x|xがファイルの場合|
|-L x|xがシンボリックリンクの場合|
|-h x|xがシンボリックリンクの場合|
|-z x|xが空文字列の場合|
|-n x|xが空文字列ではない場合|
|x === y|xとyが等しい場合(数値でも文字列でも可能)|
|x == y|xとyが等しい場合(数値でも文字列でも可能)|
|x != y|xとyが等しくない場合(数値でも文字列でも可能)|
|x < y|xがyよりも辞書順で前である場合(数値でも文字列でも可能)|
|x > y|xがyよりも辞書順で後ろである場合(数値でも文字列でも可能)|

```sh
$ test 0 == 1
$ echo $?
1    // falseとして1が返る

$ [ 0 == 1 ]
$ echo $?
0     // trueとして0が返る

$ [ 'hoge' \< 'fuga' ]  // クウォーティング必須。 '<'でも可
$ echo $?
1     // falseとして1が返る
```

ちなみに、`$?`は直前のコマンドの実行結果を取得するコマンド。

また、これらを複雑に条件づけるために、以下の演算子も存在する

|演算子|処理|
|:--:|:--:|
|式a -a 式b|aかつb。よくある(a&b)|
|式a -o 式b|aまたはb。よくある(a|b)|

#### 最後に`if [[条件っぽいもの]]; then ...`

これは、testコマンドをさらに拡張したものみたいです

簡単にいうと、先ほどの`-a`や大なり小なりのクウォーティングが不要になるんです。

testコマンド時▼



`[[]]`時▼

```sh
$ [ 'hoge' < 'fuga' && 1 == 0]
$ echo $?
1
```
後者の方がわかりやすいですねえ。こんな書き方ができるのは、`[[]]`がコマンドではないためらしいです。

しかも、`[[]]`の場合には、`&&`の前者で1(false)だった場合は後者の処理は行わないので使いやすいですね。なので先ほどの処理では実は`1 == 0`の処理は行われていないんです！

## まとめ
- まだ半分ぐらいしか読んだこと書いていないけどこれでだいたい内部のシェル読める気がする(読めるとは言っていない)
- 残り半分は気が向いたら書く
- 参考資料
    - 続きがきになる方はこちらからどうぞ▼
        - https://www.amazon.co.jp/%E6%96%B0%E3%81%97%E3%81%84%E3%82%B7%E3%82%A7%E3%83%AB%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8-%E4%B8%89%E5%AE%85-%E8%8B%B1%E6%98%8E/dp/4797393106
        - https://www.amazon.co.jp/%E6%94%B9%E8%A8%82%E7%AC%AC3%E7%89%88-%E3%82%B7%E3%82%A7%E3%83%AB%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E5%9F%BA%E6%9C%AC%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9-sh%E3%81%A7%E3%80%81%E3%81%93%E3%81%93%E3%81%BE%E3%81%A7%E3%81%A7%E3%81%8D%E3%82%8B-PRESS-plus/dp/4774186945/ref=pd_lpo_sbs_14_t_1?_encoding=UTF8&psc=1&refRID=T19XWWZ1EM0HA1RAPMYX