---
# System prepended metadata

title: Shell 基礎
tags: [Linux, Shell Script, Script]

---

# Shell基礎
> 寫Shell的路上，不孤單，有電腦會陪伴我們
## 基礎
:::spoiler 第一站:Hello World
```bash=
#!/bin/bash
echo "Hello World"
```
* `#!/bin/bash`：告訴系統該用哪個編譯
* `echo "Hello World"`：類似print，在終端印出文本訊息
:::

:::spoiler 第二站:特殊字元
1. `#`註釋
2. `;`命令分隔符(一行可以有多個命令，前面要空格)
3. `;;`條件語句終止符(case專用的，後續會介紹)
4. `:`空命令，類似true(總是返回退出狀態0，且不執行操作)
5. `$`取值符號，取出變量內容
6. `()`命令組，裡面的變量算是區域變數，父shell不可見
:::

:::spoiler 第三站:特殊字符『-』part1
```bash=
(cd /usr/src && tar cf - . ) | (cd /usr/dst && tar xpvf -)
```
* `cd /usr/src`：這一段代表到此目錄下
* `&&`：前一段執行成功才執行下一段
* `tar cf - .` : 將目錄下所有(.)壓縮成標準輸出(-)因為有`｜`所以將輸出給下一個命令
* `tar xpvf -`：解壓縮來自標準輸入的資料(-)
* 本腳本主要將src目錄下所有檔案及資料夾搬移到dst
:::
:::spoiler 第四站:特殊字符『-』part2
```bash=
#!/bin/bash
BACKUPFILE=backup-$(date +%m-%d-%Y)
archive=${1:-$BACKUPFILE}
tar cvf - `find . -mtime -1 -type f -print` > $archive.tar
gzip $archive.tar
echo "Directory $PWD backed up in archive file \"$archive.tar.gz\"."
exit 0
```
* `BACKUPFILE=backup-$(date +%m-%d-%Y)`：命名變數，以日期命名
* `archive=${1:-$BACKUPFILE}`：如果沒有在指令中特別命名，則用預設
* `tar cvf - find . -mtime -1 -type f -print > $archive.tar`：找到過去一天內有更動的檔案(非資料夾)進行壓縮成tar檔
    * `-mtime n`：n天前
    * `-mtime -1`：1天內
    * `-mtime +1`：超過1天
    * `-type f`：檔案
    * `-type d`：資料夾
:::
:::spoiler 第五站:變量與參數
* `不要混淆 = 與 -eq，後者用來進行比較而非賦值。`
* `一個未被賦值或未初始化的變數擁有空值（null value）。注意：null值不等同於0。`
* `''`：單引號完全不解析包覆的內容
* `""`：雙引號會展開變數命令及特殊(換行)符號等
* 變量替換
```bash=
a=375 
hello=$a #hello取a得值
echo hello #不加 $，會直接印出字串：hello
echo $hello #會展開變數 hello 的值，也就是：375
echo ${hello} #與上面相同，使用花括號可更清楚變數邊界：375
echo "$hello" #加雙引號也會展開變數值：375
echo "${hello}" #同上，雙引號+花括號：375

echo

hello="A B  C   D"
echo $hello
#沒有引號，bash 會把空格當作參數分隔，結果是：A B C D（多餘空格被壓縮）
echo "$hello" 
#有雙引號，整個字串會被當作一個單位保留空格：A B  C   D

echo

echo '$hello' 
#單引號不會展開變數，會原樣輸出：$hello
```

---

* 變量賦值
```bash=
#!/bin/bash
a=879 #這時候a是字串
echo "The value of \"a\" is $a."
let a=16+5 #這時bash會當a為整數做加法處理
echo "The value of \"a\" is now $a."

echo

echo -n "Values of \"a\" in the loop are: "
for a in 7 8 9 11
do
  echo -n "$a "
done

echo

echo -n "Enter \"a\" "
read a #這時候a讀取輸入的值
echo "The value of \"a\" is now $a."

exit 0
```
* `這裡補充弱型態，在Bash中，變量始終以字符串儲存，但在使用let或(())等數學運算方法時，會被解析為數字，使用 declare -i 可使其在進行賦值等操作時視為整數，並可透過 unset 清除型態設定。`
:::
:::spoiler 第六站:弱型態與特殊變量
* Bash弱型態
    * 儲存上永遠是字串型態的內容
    * 透過計算可以將變數(暫時視為整數)
    * 透過declare -i 可強制將變數未來的賦值行為視為整數
    * 在數學運算中空值視為 0
    * unset會清除該變數，從記憶體拔掉
* 環境變量(導出)
```bash=
export 變數名稱
#export將變量標記為環境變量，可被子進程繼承，並在當前 Shell 環境中生效
```
* 位置参数
    * `$0`：腳本名稱`$n`：開始為第n個參數(10以後要加{$10})
```bash=
args=$#           # 取得參數的個數（位置參數個數），例如傳入 3 個參數，args 就是 3
lastarg=${!args}  # 使用間接參照，取得最後一個參數的值
```
:::
:::spoiler 第七站:測試:part1
```bash=
if [...]
fi
```
* `[`是 test 命令，根據括號內的條件判斷結果。當只有單個字符串時，非空字符串（如 [ "0" ]、[ "xyz" ]）為 true，空字符串（如 [ "" ] 或未宣告的變量 [ $a ]）為 false。其他用法包括數值比較（如 -eq）、字符串比較（如 =）、文件測試（如 -f）等。
* `if [0]`：為true，0為字串
* `if [-1]`：為true，-1為字串
* `if []`：為false，為空字串
* 未宣告的情況`if [$a]`：為false，空值
```bash=
if [...]; then #if和then在同一行要用;分開
#elif = else if
```
* `if [ 條件 ]`：括號內為test語法，裡面塞條件判斷
    * `-eq`：等於 `-ne`：不等於
    * `-lt`：小於 `-le`：小於等於
    * `-ge`：大於 `-ge`：大於等於
    * 以上陳列出數字比較，另有檔案及邏輯的條件判斷
* `[[]]`：雙括號內有更多的使用彈性(bash)內部數值比較會直接轉換8&16進位後再比
* `(())`：此運算出來的結果與`[]`相反，為真(非0)⇒ exit status 為 0，為假則是 1
    * `(( 0 ))`：1
    * `(( 1 ))`：0
    * `(( 5 > 4 ))`：0
    * `(( 5 > 9 ))`：1
:::
:::spoiler 第八站:測試:part2
* `[[]]`：這個在做比較時，需要空白[[ a > b ]]
* `(())`：這個只做運算比較，不用空白((a>b))
* 文件測試部分陳列～語法`[ -e a.file ]`
    * `-e`：文件是否存在
    * `-f`：是否為常規文件(非目錄)
    * `-s`：文件大小不為0
    * `-d`：是否為目錄
    * `-r`：是否可讀`-w`：可寫`-x`：可執行
    * `f1 -nt f2`：f1比f2新
    * `f1 -ot f2`：f1比f2舊
* 邏輯比較
    * `-a`：AND運算
    * `-o`：OR運算
    * 範例～`[ "$a" -gt 0 -a "$b" -gt 0 ]`ab均大於0的話

* 邏輯比較補充
* `&&`：這個AND運算要在`[[]]`裡面使用
* `||`：這個OR運算要在`[[]]`裡面使用
:::
:::spoiler 第九站:運算符
* 運算大致與程式語言相同
    * `&`和`｜`：都是做算術運算的，會轉為二進制去做
    * `&&`和`||`：這兩個才是邏輯判斷的，小心弄錯
    * `^`：這個是XOR運算，**沒有XOR邏輯判斷**`^^`
    * `!`：邏輯運算NOT
* 特殊運算符`,`
    * `$((1+1,a=2*2,3/3))`
    * 每段都會計算，`echo $a`會印4
    * `$`取值會取最後一個3/3的答案1
* 數字運算符
    * `0數字`：8進位
    * `0x數字`：16進位
    * `let "a = 進制#數字"`：轉譯成十進位a
:::

---

下一站 ：[Shell 進階](https://hackmd.io/a1Pm_wSlRmaKcmji61DiJg)