Shell Tips
記錄一些最近寫 Shell 用到的語法
ShellCheck
如果你是使用 Visual Studio Code 強烈建議可以安裝這個套件 ShellCheck。 可以幫你檢查 Shell 的錯誤跟提供建議的寫法。
shebang
相關定義可以參考 維基百科
shebang 必須寫在 shell 的第一行,用來指示這個腳本使用的直譯器。
#! /bin/bash
echo "SHELL = $SHELL"
echo "MY SHELL = $(ps -o comm -p $$ | awk 'NR==2 { print $1}')"
雖然可以用環境變數 SHELL
取得使用的 Shell 不過並不是很正確。所以需要用其他命令取得正確的 Shell。
在我的電腦上印出的是使用者預設的 shell 而不是 shebang 指定的 shell。使用 ps 才能拿到正確的 shell。
SHELL = /bin/zsh
MY SHELL = /bin/bash
常用的內建變數
$?
: 上一個指令或程式執行的回傳值,通常 0 代表執行成功,0 以外的數字表示失敗$n
: n 是數字,表示第幾個參數$#
: 參數數量$$
: shell 執行的 PID- 如果要取得 pipeline 的其中一個階段的結果可以用
${PIPESTATUS[n]}
其中的 n 是數字
redirect
通常會用 > info.log
來把輸出寫到檔案,我們也可以用來將輸出寫到不同的目標 2> error.log | tee output.log
。
2
是 stderrtee
用來把輸出同時寫到畫面跟檔案
如果要隱藏輸出可以使用 > /dev/null
執行外部命令
在 shell 中可以透過直接執行程式來設定變數
RBENV_VERSION=`rbenv version | tr '{print $1}'`
echo "$RBENV_VERSION"
不過 ShellCheck 會建議不要使用 ‘`’ 的方式,可以改用以下寫法:
RBENV_VERSION=$(rbenv version)
echo "$RBENV_VERSION"
判斷式
基本的 if 語法是這樣
if [ 條件 ]
then
# do something
elif [ 條件 ]
then
# do something
else
# do something
fi
- NOTE
[
or]
跟條件需要有空格分開
常用的條件有:
字串比對
"字串1" = "字串2"
: 比對兩個字串是否相同- Shellcheck 會建議即使是變數也要加上
"
因為變數的值可能有空格或特殊符號,有可能在特定輸入會出問題- ❌
if [ KEY = "name" ]
- ⭕
if [ "$KEY" = "name" ]
- ❌
- Shellcheck 會建議即使是變數也要加上
"字串1" != "字串2"
: 比對兩個字串是否不ㄍㄜ同
數值比對
變數1 -eq 變數2
: 比對兩個數字是否相同變數1 -ne 變數2
: 比對兩個數字是否相同變數1 -lt 變數2
: 變數1 < 變數2變數1 -gt 變數2
: 變數1 > 變數2變數1 -le 變數2
: 變數1 <= 變數2變數1 -ge 變數2
: 變數1 >= 變數2
檔案檢查
-d 路徑
: 檢查路徑是否為資料夾-e 路徑
: 檢查路徑的檔案或資料夾是否存在
NOTE: NOT 的語法是在條件前面加上 !
,要檢查檔案不存在就是 if [ ! -e "test.ini" ]
多條件比對
多個條件可以用 ||
或是 &&
分隔多個條件
Ex. if [ $? -eq 0 ] && [ -e "test.ini" ]
判斷其他命令的執行結果
方法一
rbenv version
if [ $? -eq 0 ]
then
# Do something
fi
方法二
if rbenv version
then
# Do something
fi
可以直接在 if 的條件式中執行命令。這樣比較簡潔, ShellCheck 建議使用這種格式。
使用者輸入
echo "What's your name?"
read varname
echo "Hello $varname"
畫面會輸出一個等待輸入的提示,等待使用者輸入。
What's your name?
☐
等到輸入字串並按下 Enter 送出之後,變數的值就被設定為使用者所輸入的字串 (不包含 Enter)
What's your name?
Caesar
Hello Caesar
有用的參數
我們可以在 read
加上 -p
表示 read
要顯示的提示字串,就不需要用 echo
。
也可以加上 r
指定不將反斜線視為跳脫字元。
read -rp "What's your name? " varname
echo "Hello $varname"
需要注意的是,使用 -p
時的提示字串跟輸入游標沒有換行,需要自行加上空格排版,
What's your name? ☐
如果要用密碼輸入的話可以用 -s
來隱藏輸入。
因為 -s
會將送出的 Enter 也隱藏,所以顯示時可以加上一個空行來排版。
read -rp "username? " username
read -srp "password? " password
echo ""
echo "Hello $username"
如果要實作 “press any key to continue” 可以用以下的語法。
使用 -n 1
來指定只要讀取 1 個字元。
read -rp "username? " username
read -n 1 -srp "press any key to continue" key
echo ""
echo "Hello $username"
同樣的我們也可以用這個方法來實作選項。
read -rp "username? " username
read -n 1 -rp "Are you $username (y/n)? " key
echo ""
if [ "$key" = "y" ]
then
echo "Hello $username"
fi
Color
Shell 輸出的時候可以加上控制碼改變文字或是背景的顏色, 我們在寫一些較長的腳本會希望輸出的地方能有色彩變化, 這樣在閱讀的時候比較容易找到重要的地方。
色碼的顏色文字跟背景的顏色可以分別設定。
控制碼
控制碼的格式為 \033[xxm
其中的 xx 就是顏色代碼。
使用的時候可以用 echo -e
指定後面的文字帶有控制碼。
另外,要注意的是結束的時候需要用 \033[0m
這個特殊的控制碼 (RESET) 將所有格式清空。
否則格式會影響到後面的輸出。
echo -e "\033[31mhello\033[0m world"
以上代碼就會顯示出紅色的 hello 而 world 則是顯示預設的文字顏色。
前景 foreground
前景色一共有 16 色
\033[30m
: 黑色 (Black)\033[31m
: 紅色 (Red)\033[32m
: 綠色 (Green)\033[33m
: 黃色 (Yellow)\033[34m
: 藍色 (Blue)\033[35m
: 洋紅色 (Magenta)\033[36m
: 青色 (Cyan)\033[37m
: 白色 (White)\033[1;30m
(or\033[90m
): 亮黑色 (Bright Black / Gray)\033[1;31m
(or\033[91m
): 亮紅色 (Bright Red)\033[1;32m
(or\033[92m
): 亮綠色 (Bright Green)\033[1;33m
(or\033[93m
): 亮黃色 (Bright Yellow)\033[1;34m
(or\033[94m
): 亮藍色 (Bright Blue)\033[1;35m
(or\033[95m
): 亮洋紅色 (Bright Magenta)\033[1;36m
(or\033[96m
): 亮青色 (Bright Cyan)\033[1;37m
(or\033[97m
): 亮白色 (Bright White)
背景 background
背景色一共有 16 色,可以用分號分隔就跟前景色一起使用。
\033[40m
: 黑色 (Black)\033[41m
: 紅色 (Red)\033[42m
: 綠色 (Green)\033[43m
: 黃色 (Yellow)\033[44m
: 藍色 (Blue)\033[45m
: 洋紅色 (Magenta)\033[46m
: 青色 (Cyan)\033[47m
: 白色 (White)\033[1;40m
(or\033[100m
): 亮黑色 (Bright Black / Gray)\033[1;41m
(or\033[101m
): 亮紅色 (Bright Red)\033[1;42m
(or\033[102m
): 亮綠色 (Bright Green)\033[1;43m
(or\033[103m
): 亮黃色 (Bright Yellow)\033[1;44m
(or\033[104m
): 亮藍色 (Bright Blue)\033[1;45m
(or\033[105m
): 亮洋紅色 (Bright Magenta)\033[1;46m
(or\033[106m
): 亮青色 (Bright Cyan)\033[1;47m
(or\033[107m
): 亮白色 (Bright White)
其他控制符號
\033[0m
: 重置 (Reset)\033[1m
: 粗體 (Bold)\033[4m
: 底線 (Underline)\033[7m
: 反轉前景、背景色 (Reverse)
另外也可以用來控制游標位置,做到類似進度條的顯示。 細節請參考參考資料。