<style>
.reveal {
font-size: 24px;
font-family: 'Source serif pro', '源樣明體';
}
.reveal pre code {
font-size: 20px;
}
.reveal pre {
line-height: 24px;
}
.reveal h1, .reveal h2, .reveal h3 {
font-family: 'Source sans pro', '源樣黑體';
}
em {
color: #ED8;
}
</style>
# 關於 Shell Script
---
## 殼層 Shell
讓使用者可以享受到 OS 提供的各種服務的介面。
* 命令列介面 (Command-Line Interface, CLI)
* 圖形使用者介面 (Graphical User Interface, GUI)。

----
## Unix Shell
就是在 Unix 系統內可以使用的 Shell。
https://en.wikipedia.org/wiki/Comparison_of_command_shells
* sh -- Borne Shell (1979)
* <em>bash</em> -- Borne Again Shell (1987)
* csh, tcsh -- C Shell (1978)
* fish -- Friendly Interactive Shell (2005)
* zsh -- Z Shell (1990)
今天的重點是 bash。
----
## 腳本語言 Scripting Language
直譯器 (Interpreter) 會按照腳本指令逐行執行。
* 簡單且方便的小工具
----
## 內建指令 vs 外部程式
只要不是 Builtin command 的都是外部程式
https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html
* 比方說 `cd`, `echo`, `alias` 是內建指令
* 比方說 `ls`, `ps`, `grep` 是外部程式
* 可以用 `which` 這支程式找出外部程式的位置
_為什麼 bash 找得到外部程式?_
----
## 環境變數 Environment Variables
bash 提供自訂環境變數的功能,如果要存取這些環境變數,可以透過 `$` 符號加上變數名稱。通常 bash 裡面的環境變數名稱是習慣全大寫的。
```shell
$ echo $PATH
```
----
## 程式 Program vs 程序 Process
環境變數就是定義在 bash 跑起來這個程序(process)裡面的變數啦。如果把 `$PATH` 改掉了,可能就找不到外部程式了。
```shell
$ PATH=''
$ which ls
bash: which: No such file or directory
```
----
## 列出環境變數
讓我們來執行看看 `printenv` 這支程式,它可以幫我們列出現有的環境變數。
```shell
$ printenv
```
覺得很難看怎麼辦?讓我們試著寫第一個腳本,把它上色。
---
# Example 1
------
<font size=200%>Colorize `printenv`</font>
----
## 簡單實作 `cat`
按照標準輸入的內容直接輸出。
```bash
#!/usr/bin/env bash
while read line
do
echo "$line"
done
```
`#!` 被稱為 shebang (或是 hashbang),通常出現在第一行可以讓 bash 知道你要用什麼環境或程式來執行這段腳本。
https://zh.wikipedia.org/wiki/Shebang
<!-- .element: class="fragment" data-fragment-index="1" -->

</div>
<!-- .element: class="fragment" data-fragment-index="1" -->
----
## 加上行號
```bash=
#!/usr/bin/env bash
# 除了印出來以外,還加上現在是第幾行。
linenumber = 0
while read line
do
linenumber += 1
echo "Line $linenumber: $line"
done
```
_哪裡出問題了?_
----
## 空白很重要
* `linenumber = 0` 的意思是,執行一支叫做 linenumber 的程式,然後傳入兩個參數 `=` 和 `0`。
* `linenumber= 0` 的意思是,設定變數 `linenumber` 是空白,然後執行名為 `0` 的程式。
```bash=
#!/usr/bin/env bash
# 除了印出來以外,還加上現在是第幾行。
linenumber=0
while read line
do
linenumber+=1
echo "Line $linenumber: $line"
done
```
_哪裡出問題了?_
----
## 用雙括弧代表算術運算
也可以用 `expr` 或是 `let` 指令。
```bash=
#!/usr/bin/env bash
# 除了印出來以外,還加上現在是第幾行。
linenumber=0
while read line
do
((linenumber += 1))
echo "Line $linenumber: $line"
done
```
或者也可以把 linenumber 宣告為整數。
```bash=3
declare -i linenumber=0
```
----
## 對輸入的每一行以等號分隔
想法:字串 $\to$ (split) 陣列 $\to$ (join) 把後面的黏起來
```bash=4
# 用 "=" 當作分隔符號 delimiter 把字串分成好幾節
IFS="=" fields=($line)
# 把第二節以後的黏回來
printf -v var "%s=" "${fields[@]:1}"
var=${var%?}
```
----
## 加上顏色
* https://tforgione.fr/posts/ansi-escape-codes/
* https://askubuntu.com/questions/821157/print-a-256-color-test-pattern-in-the-terminal

----
## 加上顏色
```bash=
#!/usr/bin/env bash
# 檔案名稱取名為 02-colorize-kv.sh
while read line
do
# 用 "=" 當作分隔符號 delimiter 把字串分成好幾節
IFS="=" fields=($line)
# 把第二節以後的黏回來
printf -v var "%s=" "${fields[@]:1}"
var=${var%?}
# 把第一節加上顏色,加上第二節的資料
echo -e "\x1b[38;5;220m${fields[0]}\x1b[38;5;240m=\x1b[0m$var"
done
```
----
## 把腳本當作程式執行 (會開啟新環境)
```shell
$ printenv | ./02-colorize-kv.sh
```
----
## 小結:我們討論了什麼?
* 環境變數
* 變數指派
* 變數運算
* $ 展開字元
* 字串切成陣列、IFS 分隔符號環境變數
* echo 和 printf
* 文字上色
* | pipe
---
# Example 2
------
<font size=150%>Display Time in Different Time Zone</font>
Wicked Cool Shell Scripts #21
----
## 時區 Timezone

https://en.wikipedia.org/wiki/Tz_database
搭配 `TZ="時區檔案名稱" date` 指令服用。
----
## 首先載入 Zoneinfo
```bash=9
zonedir="/usr/share/zoneinfo"
if [[ ! -d $zonedir ]] ; then
echo "No time zone database at $zonedir." >&2
exit 1
fi
```
* https://mywiki.wooledge.org/BashGuide/SpecialCharacters
{"metaMigratedAt":"2023-06-16T11:57:54.652Z","metaMigratedFrom":"YAML","title":"Shell Scripts","breaks":true,"slideOptions":"{\"slideNumber\":true,\"theme\":\"night\",\"transition\":\"convex\"}","contributors":"[{\"id\":\"a5ccd730-ffe0-47d3-a6b2-e31e1fcd4e37\",\"add\":4847,\"del\":584}]"}