範圍:Functions ~ Prompting
[TOC]
## Functions
### 語法
<pre>
<font color=#3c78d8>function</font> fname <font color=#3c78d8>( )</font>
{
action;
<font color=#3c78d8>return int;</font>
}
</pre>
- function 名字不能有引號及 $
### Example
```bash=
function f1 () {
echo 'This is f1.'
}
# 省略 ()
function f2 {
echo 'This is f2.'
}
# 省略 function
f3 (){
echo 'This is f3.'
}
# 省略 () 及 function
# f4: command not found
# 無法判斷是不是 function
f4 {
echo 'This is f4.'
}
```
### Return Value
```bash=
#!/bin/bash
f1(){ echo 'function 1'; }
f2(){
echo 'function 2'
return 22
}
f1
echo \$? is $?
f2
echo \$? is $?
```
- 如果沒有用 return 且 function 正常執行,回傳 0 (exit status)
### 傳遞參數
- `vi para.sh`
- `./para.sh 1 2 3`
```bash=
#!/bin/bash
func (){
echo func
for i in $@
do
echo ${i}
done
echo end of func
}
for i in $@
do
echo ${i}
done
func h e l l o
```
- 呼叫 func 時傳入的參數,不等於執行 `para.sh` 傳入的參數
### Local Variables
```bash=
#!/bin/bash
f1(){
echo exeu function 1
num=1 #global
}
f2(){
echo exeu function 2
local num=2 #local
}
echo set num=0
num=0
f1
echo num=$num
echo set num=0
num=0
f2
echo num=$num
```
- 在 function 中,如果要使用 local variables,需在宣告變數時,加上 `local`
### 列出所有 function
- 完整列出 function
- `declare -f`
- `typeset -f`
- 只列出 function 名稱
- `declare -F`
- `typeset -F`
### 刪除 function
- `unset -f fname`
```bash=
f_unset(){ echo 'test unset'; }
f_unset
#test unset
unset -f f_unset
f_unset
#f_unset: command not found
```
---
## Arithmetic Evaluation
- 幾乎與 C 語言一樣
- 算式記得用 `(())`, `$(())` 包住
```bash=
#!/bin/bash
num1=1
num2=2
num3=3
((num3++))
echo "num1 + num2 = $((num1 + num2))"
echo "2 ** 5 = $((2 ** 5))"
echo num3 = $num3
```
<!-- - :::spoiler Ans.
$((…)) Arithmetic expansion
does math 。Arithmetic expansion returns the result of mathematical operations
((…)) Arithmetic evaluation
does math 。performs calculations and changes the value of variables. Is done without $
::: -->
### 宣告變數為整數
- `declare -i var`
- 如果沒有宣告成整數,可能會出現非預期的結果
```bash=
#!/bin/bash
num1=1
num2=2
declare -i int1=1
declare -i int2=2
num1+=$num2
echo $num1
int1+=int2
echo $int1
```
> 12
> 3
---
## Conditional Expressions
- True <font color=#3c78d8> ➜ return 0</font>
- Flase <font color=#3c78d8> ➜ return 1</font>
### 判斷某個檔案的狀態
:::spoiler 一覽
|||
|-|-|
| -a | True if file exists.
| -b | True if file exists and is a block special file.
| -c | True if file exists and is a character special file.
| -d | True if file exists and is a directory.
| -e | True if file exists.
| -f | True if file exists and is a regular file.
| -g | True if file exists and is set-group-id.
| -h | True if file exists and is a symbolic link.
| -k | True if file exists and its ''sticky'' bit is set.
| -p | True if file exists and is a named pipe (FIFO).
| -r | True if file exists and is readable.
| -s | True if file exists and has a size greater than zero.
| -t | True if file descriptor fd is open and refers to a terminal.
| -u | True if file exists and its set-user-id bit is set.
| -w | True if file exists and is writable.
| -x | True if file exists and is executable.
| -O | True if file exists and is owned by the effective user id.
| -G | True if file exists and is owned by the effective group id.
| -L | True if file exists and is a symbolic link.
| -S | True if file exists and is a socket.
| -N | True if file exists and has been modified since it was last read.
:::
#### 檔案是否存在
```bash=
touch file1 #建立空白檔案
[[ -a file1 ]]
echo $?
[[ -a file2 ]]
echo $?
```
#### 檔案大小是否大於零
```bash=
touch file #空白檔
ls -l file # 查看檔案大小
[[ -s file ]]
echo $?
```
### 比較檔案(根據修改時間)
#### file1 比 file2 新 <font color=#3c78d8>or file1存在 file2 不存在</font>
```bash=
touch file1
touch file2
[[ file1 -nt file2 ]] #newer than
echo $?
rm file2
[[ file1 -nt file2 ]]
echo $?
```
#### file1 比 file2 舊 <font color=#3c78d8>or file1不存在 file2 存在</font>
```bash=
touch file1
touch file2
[[ file1 -ot file2 ]] #older than
echo $?
rm file2
[[ file1 -ot file2 ]]
echo $?
```
#### Question
- `touch file{1,2}` 哪個檔案比較新?
- :::spoiler Ans.
nt, ot 都回傳 Flase
(zsh 結果不同,猜測可能是精確度的問題)
:::
### 比較 arguments
- `arg1 OP arg2`
- arg1, arg2 都要是整數
- OP
- `-eq` 表示 ==
- `-ne` 表示 !=
- `-lt` 表示 <
- `-le` 表示 <=
- `-gt` 表示 >
- `-ge` 表示 >=
#### Example
```bash=
#!/bin/bash
if [[ $# < 2 ]] ;then
echo argc should gather than 1
else
if [[ $1 -eq $2 ]]; then
echo $1 \=\= $2
elif [[ $1 -lt $2 ]]; then
echo $1 \< $2
elif [[ $1 -gt $2 ]]; then
echo $1 \> $2
fi
fi
```
---
## Exit Status
- The exit status of an executed command is the value returned by the **waitpid system call** or **equivalent function**.
- between 0 and 255
- The shell may use values above 125 specially.
### Example
#### 指令成功執行 <font color=#3c78d8> ➜ return 0</font>
```bash=
ls
echo $?
```
#### command not found <font color=#3c78d8> ➜ return 127</font>
```bash=
a
echo $?
```
#### command is found but is not executable <font color=#3c78d8> ➜ return 126</font>
```bash=
touch notexecutable.sh
./notexecutable.sh
echo $?
```
#### 錯誤使用 Shell 內建指令 <font color=#3c78d8> ➜ return 2</font>
```bash=
cd -h
#-bash: cd: -h: invalid option
#cd: usage: cd [-L|[-P [-e]] [-@]] [dir]
echo $?
```
---
## Job Control
### jobs
- `jobs` - 查看目前程序運作狀態

- `jobs -l` - 多出 PID

- `jobs -p` - 只有 PID

- `jobs -r` - 只印 Running

- `jobs -s` - 只印 Stopped

### 背景執行
- `<command> &`
```bash=
sleep 10 &
jobs
```
### 暫停程序
- `Crtl-Z` 可以將程序暫停
```bash=
sleep 10
^Z
jobs
```
### jobspec
- The character % introduces a job specification (jobspec).
#### %n
- n 為 job number
#### %ce
```bash=
sleep 10 ^Z
read &
jobs %r
jobs %s
```
#### %+, %-
- `%+` - 代表最後一個被暫停的程序
- `%-` - 代表倒數第二個被暫停的程序
```bash=
sleep 10 ^Z
sleep 20 ^Z
sleep 30 ^Z
jobs
```

### 繼續執行被暫停的程序
#### 在背景繼續執行
```bash=
# bg <job number>
sleep 10
^Z
jobs
bg 1
```
```bash=
# %[] &
sleep 10
^Z
jobs
%1 &
```
#### 移至前景繼續執行
```bash=
# fg <job number>
sleep 10
^Z
jobs
fg 1
```
### kill
- `kill %[]`
- 刪除背景程序
### pipeline

---
## Prompting
- BASH 可以使用跳脫字元自訂 PS1, PS2
### `$` or `#`
```bash=
PS1='\$ '
```
- root 顯示 `#`
- 其他 user 顯示 `$`
### Bell
```bash=
PS1='\a\$ '
# or
PS1='\007\$ '
```
### Time
#### a. Date
```bash=
PS1='\d \$ '
```
#### b. Current Time
```bash=
# 24-hour HH:MM:SS format
PS1='\t \$ '
# 12-hour HH:MM:SS format
PS1='\T \$ '
# 12-hour am/pm format
PS1='\@ \$ '
# 24-hour HH:MM format
PS1='\A \$ '
```
#### c. 自訂格式
```bash=
PS1='\D{} \$ '
PS1='\D{%M:%H} \$ '
PS1='\D{%h %d %a} \$ '
```
- {} 內的格式可以查看 [strftime(3)](https://linux.die.net/man/3/strftime)
### Color
```bash=
PS1='\e[32mHello!\e[0m > '
PS1='\e[1;32mWorld!\e[0m > '
PS1='\e[4;34;46mBASH!!\e[0m > '
```
- 指定顏色
- \e\[<font color=#3c78d8>文字顏色</font>m
- \e\[<font color=#3c78d8>文字樣式</font>;<font color=#3c78d8>文字顏色</font>;<font color=#3c78d8>背景顏色</font>m (沒有順序性,樣式可以多選)
- :::spoiler 文字樣式
0 一般樣式
1 高亮度
4 底線
5 灰底
7 反白
:::
- :::spoiler 文字顏色
30 黑色
31 紅色
32 綠色
33 黃色
34 藍色
35 紫色
36 青綠色
37 白色
:::
- :::spoiler 背景顏色
40 黑色
41 紅色
42 綠色
43 黃色
44 藍色
45 紫色
46 青綠色
47 白色
:::
- 還原原始顏色
- \e\[0m
### History Number & Command number
```bash=
PS1='\! \$ '
PS1='\# \$ '
# 比較差別,可以重新連線再檢查一次
PS1='\! \# \$ '
```
- Command number 只有這次 Session 的 command seq
- History Number 包含過去的 command 紀錄
### Exercise

- Hint
- (高亮度+底線+紫色) history
- (反白) 完整星期
- (無樣式) $ or #
- (藍色)
- :::spoiler Ans.
`PS1='\e[0m\e[1;4;35m\!\e[0m \e[7m\D{%A}\e[0m \$ \e[36m'`
:::