# TLCL 輪読会
### 27 – Flow Control: Branching with if
2023/08/15 [@kdnakt](https://twitter.com/kdnakt)
---
## 今日の範囲
### 27 – Flow Control: Branching with if
- if
- Exit Status
- test
- A More Modern Version of test
- (( )) - Designed for integers
- Combining Expressions
- Control operators: Another Way to Branch
---
### if
- シンプルな例
```bash
x=5
if [ "$x" -eq 5 ]; then
echo "x equals 5."
else
echo "x does not equal 5."
fi
```
----
- 複雑な例
```bash
if commands; then
commands
[elif commands; then
commands ...]
[else
commands]
fi
```
---
### Exit Status
- コマンドが返す値
(スクリプトやシェル関数を含む)
- 0〜255
- 0が成功、それ以外は失敗
- 詳細はman pageに
```bash!
$ ls -d /usr/bin
/usr/bin
$ echo $?
0
$ ls -d /bin/usr
ls: cannot access /bin/usr: No such file or directory
$ echo $?
2
```
----
- ビルトインコマンド
- `true`:常に終了ステータス0
- `false`:常に終了ステータス1
```bash
$ if true; then echo "It's true."; fi
It's true.
# 複数コマンドの場合は最後のコマンドの結果で分岐
$ if true; false; then echo "It's true."; fi
$
```
---
### test
- ifで最頻出のコマンド:test
- 以下2つは同じ意味
- test *expression*
- [ *expression* ]
- bashでは`[`はビルトインコマンド
----
#### File Expressions
- `f1 -ef f2`:f1とf2が同一ファイル
- `f1 -nt f2`:f1が新しい
- `f1 -ot f2`:f1が古い
- `-b file`:存在するブロックデバイス
- `-c file`:存在するキャラクタデバイス
- `-d file`:存在するディレクトリ
- `-e file`:存在する
- `-f file`:存在するファイル
----
- `-g file`:存在し、setgidアクセス権あり
- `-G file`:存在し、実効グループID所有
- `-k file`:存在し、スティッキービットあり
- `-L file`:存在するシンボリックリンク
- `-O file`:存在し、実効ユーザーID所有
- `-p file`:存在する名前付きパイプ
- `-r file`:存在し読み込み可能
- `-s file`:存在し長さ1以上
- `-S file`:存在するネットワークソケット
----
- `-t fd`:端末でオープンされているfd
- `-u file`:存在し、setuidアクセス権あり
- `-w file`:存在し書き込み可能
- `-x file`:存在し実行可能
----
```bash!
FILE=~/.bashrc
if [ -e "$FILE" ]; then
if [ -f "$FILE" ]; then
echo "$FILE is a regular file."
fi
# 省略
else
# 省略
fi
```
----
#### String Expressions
- `str`:strがnullでない
- `-n str`:strの長さが1以上
- `-z str`:strの長さが0
- `str1 = str2`:等しい。POSIX標準。
- `str1 == str2`:等しい。bash版。
- `str1 != str2`:等しくない
- `str1 > str2`:str1のソート順が後
- `str1 < str2`:str1のソート順が先
----
- 実例
```bash!
ANSWER=maybe
if [ -z "$ANSWER" ]; then
echo "There is no answer." >&2
exit 1
fi
if [ "$ANSWER" = "yes" ]; then
echo "The answer is yes."
elif [ "$ANSWER" = "no" ]; then
# 省略
fi
```
- `<`や`>`はリダイレクト演算子
- ""で囲むかエスケープ必要
----
#### Integer Expressions
- `int1 -eq int2`:等しい
- `int1 -ne int2`:等しくない
- `int1 -le int2`:以下
- `int1 -lt int2`:未満
- `int1 -ge int2`:以上
- `int1 -gt int2`:より大きい
----
- 実例:
```bash
#!/bin/bash
INT=-5
if [ -z "$INT" ]; then
echo "INT is empty." >&2
exit 1
fi
if [ "$INT" -eq 0 ]; then
echo "INT is zero"
else
# 次ページへ
fi
```
----
- 正負、偶数奇数判定
```bash!
if [ "$INT" -lt 0]; then
echo "INT is negative."
else
echo "INT is positive."
fi
if [ $((INT % 2)) -eq 0]; then
echo "INT is even."
else
echo "INT is odd."
fi
```
---
### A More Modern Version of test
- [[ *expression* ]]
- 新しいbashでtestの代わり
- testコマンドの式を全てサポート
- `string1 =~ regex`を実装
- `==`演算子でパターンマッチング可能
```
$ if [[ $FILE == foo.* ]]; then
> echo "$FILE は 'foo.*' パターンにマッチ"
> fi
$FILE は 'foo.*' パターンにマッチ
```
----
- 先ほどの例をリライト
```bash
INT=-5
# 拡張正規表現をサポート
if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
if [ "$INT" -eq 0 ]; then
echo "INT is zero."
else
# 省略
fi
fi
```
---
### (( )) - Designed for integers
- bashの整数演算用コマンド
- 詳細はChapter 34
- `((1))`はtrue
- `((0))`はfalse
----
- 先ほどの例をリライト
```bash!
INT=-5
# $による変数展開不要
if ((INT == 0)); then
echo "INT is zero"
else
if ((INT < 0)); then
echo "INT is negative"
else
echo "INT is positive"
fi
# 省略
fi
```
---
### Combining Expressions
- 論理演算子の違い
|演算子 |test | [[ ]]/(( )) |
|----|-----|--------|
|AND |-a | && |
|OR |-o | \|\| |
|NOT |! | ! |
----
- ANDの例
```bash!
MIN=1
MAX=100
INT=50
if [[ "$INT" -ge "$MIN" && "$INT" -le "$MAX" ]]; then
echo "$INT is within $MIN to $MAX."
else
echo "$INT is out of range."
fi
```
----
- NOTの例
```bash!
MIN=1
MAX=100
INT=50
if [[ !("$INT" -ge "$MIN" && "$INT" -le "$MAX") ]]; then
echo "$INT is outside $MIN to $MAX."
else
echo "$INT is in range."
fi
```
----
#### testか`[[ ]]`か
- testコマンド:伝統的POSIX標準
- 広く利用されている
- 可搬性があるが進歩がない
- `[[ ]]`:bashなどモダンシェルで利用可
- 使いやすくコーディングしやすい
- bashインストール必要
---
### Control operators: Another Way to Branch
- bashの制御演算子
- `c1 && c2`:c2はc1がtrue時のみ実行
- `c1 || c2`:c2はc1がfalse時のみ実行
- 実例
- `mkdir temp && cd temp`
- `[[ -d temp ]] || mkdir temp`
- `[[ -d temp ]] || exit 1`
---
### Summing Up
- 問:sys_info_pageスクリプトでパーミッションをチェックするには?
- 答:report_home_space関数を修正
```bash
report_home_space () {
if [[ "$(id -u)" -eq 0]]; then
cat << _EOF_
<h2>Home Space Utilization (All Users)</h2>
<pre>$(du -sh /home/*)</pre>
_EOF_
else
cat << _EOF_
<h2>Home Space utilization ($USER)</h2>
<pre>$(du -sh $HOME)</pre>
_EOF_
fi
return
}
```
{"slideOptions":"{\"transition\":\"slide\"}","title":"TLCL 輪読会 27章","description":"2023/08/15 @kdnakt","contributors":"[{\"id\":\"df36d0f0-b67e-41ac-96b3-f3988326d230\",\"add\":5067,\"del\":133}]"}