Shell
Frequently Used Commands
Basic
open <dir/file> # 在訪達中開啟 dir/file
start <dir/file> # DOS 中的 open
xdg-open <dir/file> # ubuntu 中的 open
clear # 清屏
cls # DOS 清屏
ls -a # 檢視當前目錄所有檔案(包括隱藏檔案)
mv <file> <dir> # 將 file 移動到 dir
mv <name1> <name2> # 重新命名
mkdir -p <dir> # 建立目錄(自動建立中間目錄)
rm <file> # 刪除檔案
rm -rf <dir> # 強制遞迴刪除
rmdir # 刪除空目錄
del # DOS 刪除命令
cat <file> # 檢視檔案內容
cat -n <file> # 顯示行號
type <file> # DOS 中的 cat
tee <file> # 將 stdin 的內容輸出到 file 中,同時將 stdin 的內容輸出到 stdout
less # 分頁顯示文字檔案內容
more # 分頁顯示文字檔案內容,比 less 功能少
touch <file> # 建立檔案
echo -e # 使用跳脫字元
echo -n # 關閉自動換行
ps # process status,顯示程序狀態
cd - # 回到上個目錄
cmp file1 file2 # 顯示兩個檔案的不同
sudo reboot # 重啟
sudo shutdown -h now # 關機
sudo systemctl start sshd # Linux 啟動 sshd 服務;比 service 命令好
sudo systemctl daemon-reload # 重新載入配置檔案
abc/
表示目錄,abc
表示檔案或目錄——當目錄存在時就是目錄,目錄不存在就是檔案。
Advanced
ln -s <source_file> <target_file> # 建立軟連結
head -n 5 <file> # 顯示 file 前 5 行內容
tail -n 5 <file> # 顯示 file 後 5 行內容
tail -f <file> # 根據檔案描述符實時監控檔案內容
tail -F <file> # 根據檔名實時監控檔案內容
read VAR # 從終端讀取使用者輸入,並賦值給 VAR 變數。
# 按下 Ctrl + D 表示讀取到檔案流的末尾
basename "/bin/zsh" # 獲取檔名 (zsh)
dirname "/bin/zsh" # 獲取目錄名 (/bin)
file --mime-encoding <file> # 獲取 file 的編碼方式 (GBK 會被當作 iso-8859-1)
iconv -f gbk -t utf-8 <file> # 將 file 以 GBK 編碼開啟,並以 UTF-8 編碼輸出到 stdin
find <file> # 在當前目錄下查詢檔案
which -a <prog> # 找到 <prog> 的所有檔案
whereis <prog> # 找到 <prog> 的所有檔案及 man 檔案
tr -d '\r' <file> # 刪除檔案中的 \r
echo $PATH | tr ':' '\n' # 將 PATH 中的 : 替換為換行符
python3 --help | grep -C 2 pip # -C 2 表示顯示匹配行的上下 2 行
pbpaste # 從剪貼簿中讀取內容
pbcopy # 將剪貼簿內容貼上進來
# npm start sf 5-2 $(pbpaste)
# Stream Editor
# -i: in-place
sed -i.bak "s/hello/world/g" <file> # 將 file 中的 "hello" 替換為 "world",並將原檔案備份為 file.bak
tree # 顯示目錄結構
arch # 顯示系統架構
nohup <prog> & # & 用於將命令作為後臺程序執行,nohup 會忽略 SIGHUP 訊號,避免程序因終端被關閉發出的 SIGHUP 訊號導致程序終止
-
sed: Command-Line Options | GNU
-
sed: The s Command | GNU
Windows 上的 pbpaste
:
pasteboard | GitHub
scoop bucket add extras
scoop install pasteboard
終端快捷鍵
⌃R
:搜尋歷史命令⌃D
: 在新行輸入,表示EOF
。在行中輸入,表示輸出“標準輸入”的緩衝區。此時連按兩下⌃D
表示EOF
⌃Z
: 掛起,可用fg
命令恢復⌃C
: 終止程序⌃V
: 字面值輸入⌃C
和⌃D
的區別:⌃C
是終止程序,⌃D
是結束輸入
執行 Shell 指令碼
以 #!
開頭的語句叫做 Shebang,用於指定指令碼直譯器。
不推薦:
#!/bin/bash
推薦:
#!/bin/env bash
在新程序中執行 Shell 指令碼
# 將 Shell 指令碼作為程式執行
# 如果不寫 ./,Linux 只會到系統路徑(由 PATH 環境變數指定)下查詢外部命令
chmod +x script.sh # 給指令碼新增執行許可權
./script.sh # 執行指令碼
# 將 Shell 指令碼作為引數傳遞給 Bash 直譯器
bash script.sh # 這種方式將忽略指令碼檔案第一行的指定直譯器資訊
在當前程序中執行 Shell 指令碼
source script.sh # 在 zsh 下依然需要使用 ./script.sh
. script.sh # source 命令的簡化寫法
Bash 配置檔案
在 /etc/profile.d
下的所有 .sh
檔案以及 /etc/profile
檔案等價於全域性的使用者 ~/.profile
檔案,會在每次登入時執行,而在 ~/.bash_profile
下的指令碼只會在第一次登入時執行。
Shell 有多個配置檔案。對普通使用者來說,~/.bashrc
是最重要的檔案,因為不管是否登陸都會載入該檔案。
Git Bash for Windows 的配置檔案位置在 C:\Program Files\Git\etc\profile.d\git-prompt.sh
Shell 配置檔案(配置指令碼)的載入 | C 語言中文網
例項
# 給 PATH 變數增加新的路徑
PATH="$PATH:$HOME/addon" # 將主目錄下的 addon 目錄也設定為系統路徑
# 修改命令提示符的格式
PS1="[bash]\$" # 命令提示符改為 [bash]$
PATH 使用冒號
:
分隔不同的路徑
PS1 和 PS2 中的特殊字元 | C 語言中文網
變數
在 Bash shell 中,每一個變數的值都是字串。在預設情況下,Bash shell 不會區分變數型別。當然,如果有必要,也可以使用 declare
關鍵字顯式定義變數的型別。
# 定義變數
variable=value # 如果要賦的值不包含任何空白符及跳脫字元,那麼可以不使用引號。不建議這種寫法
variable='value' # '' 相當於原生字串,將不解析內容中的變數、命令
variable="value" # 最常用的情況。會解析其中的變數,命令。但是不會解析跳脫字元
readonly variable # 將變數定義為只讀變數
unset variable # 刪除變數。unset 命令不能刪除只讀變數
# 使用變數
author="John"
echo $author
echo "作者是 ${author}。" # 用花括號指明變數名的邊界
⚠️ 注意: 不要使用
path
作為變數名,在大小寫不敏感的系統中path
等價於環境變數PATH
。
Globbing
Bash 會在沒有被引號包圍的命令列引數上應用檔名擴充套件。
Wildcard (萬用字元)
*
:表示任意長度的字串,除了以 .
開始的檔名和包含路徑分隔符 /
的檔名
?
:表示一個字元
假設當前目錄下有檔案 file1.txt
, file2.txt
, ...,file9.txt
,以及 filea.txt
,fileb.txt
,filec.txt
,那麼:
file[135].txt # 單字元匹配,匹配 file1.txt, file3.txt, file5.txt
file[1-9].txt # 範圍匹配,匹配 file1.txt, file2.txt, ..., file9.txt
file[!1].txt # 否定匹配,匹配 file2.txt, file3.txt, ..., file9.txt
file[1-3a-c].txt # 複合集合,匹配 file1.txt, file2.txt, file3.txt, filea.txt, fileb.txt, filec.txt
file[[:digit:]].txt # 字元類,匹配 file1.txt, file2.txt, ..., file9.txt
常見的字元類包括 [:alpha:]
、[:alnum:]
、[:lower:]
、[:upper:]
等。
說明
當使用方括號匹配時,如果模式不匹配任何檔案或目錄,它的行為將取決於 shell 的設定和選項(例如,Zsh 中的 nullglob
和 nomatch
選項影響這種行為)。
引數展開
${...}
是引數展開的一般形式,在這種形式中,你可以對變數進行各種操作。
引數展開修飾符
示例
${(o)array} # 對陣列排序
修飾符 | 含義 |
---|---|
L |
將字串轉換為小寫 |
U |
將字串轉換為大寫 |
f |
將字串按換行符分割為陣列 |
o |
陣列按字典升序排序 |
O |
陣列按字典降序排序 |
n |
陣列按數值升序排序 |
k |
展開關聯陣列的鍵 |
v |
展開關聯陣列的值 |
@ |
當陣列被引號包圍時,@ 確保每個元素被當作獨立的個體處理 |
引數替換
var=${var:-new} # 如果 var 為空或未定義,那麼將 var 替換為 new
var=${var/old/new} # 將 var 中的第一個 old 替換為 new
var=${var//old/new} # 將 var 中的所有 old 替換為 new
var=${var/#old/new} # 如果 var 的字首匹配 old,那麼將匹配的 old 替換為 new
var=${var/%old/new} # 如果 var 的字尾匹配 old,那麼將匹配的 old 替換為 new
引數替換 | 簡書
命令替換
將命令的結果賦給變數
var=`cat log.txt` # 如果有多個命令,命令之間以分號 ; 分隔
var=$(cat log.txt) # 推薦,並且這種方式最常見
Fir_File_Lines=$(wc -l $(ls | sed -n '1p')) # 兩層巢狀
# 注意,如果被替換的命令的輸出內容有換行符,或者含有多個連續的空白符,那麼在輸出變數時應該將變數用雙引號包圍,否則系統會使用預設的空白符來填充。
echo "$var"
修改變數的值時不能在變數名前加
$
,只有在使用變數時才能加$
。
=
的周圍不能有空格
變數的作用域
區域性變數
function func() {
local a=99 # 若不使用 local,則 a 是全域性變數
}
全域性變數
在當前 Shell 程序中有效。在 Shell 中定義的變數預設就是全域性變數。
環境變數
How To Read and Set Environmental and Shell Variables on Linux | DigitalOcean
在所有的子程序中有效。
export a # 將變數匯出為環境變數
export b=22 # 在定義的同時匯出為環境變數
環境變數被建立時所處的 Shell 程序稱為父程序,父程序中建立的程序叫子程序。建立子程序最簡單的方式是執行
bash
命令。透過exit
命令可以一層一層地退出 Shell。exit 可以返回命令的退出狀態:
exit 0
特殊變數
變數 | 含義 |
---|---|
$0 |
當前指令碼的檔名 |
$1 |
傳遞給指令碼或函式的引數(從 1 開始計數) |
$# |
傳遞給指令碼或函式的引數個數 |
$* |
傳遞給指令碼或函式的所有引數 |
$@ |
傳遞給指令碼或函式的所有引數 |
$? |
上個命令的退出狀態,或函式的返回值 |
$$ |
當前 Shell 程序 ID |
$1
是一種宏替換命令的退出狀態由
exit
命令返回,函式的返回值由return
返回
$*
和 $@
的區別
當 $*
和 $@
不被雙引號 ""
包含時,它們沒有任何區別。
當它們被雙引號 ""
包含時,會有如下區別:
"$*"
會將所有的引數從整體上看做一份資料,而不是把每個引數都看做一份資料。"$@"
仍然將每個引數都看作一份資料,彼此之間是獨立的。
$@
更常用
位置引數
Shell 指令碼檔案和函式可以自動接收傳入的引數,並以 $1
, $2
, ... 的方式表示。
如果引數個數達到了 10 個,那麼需要用
${10}
的方式指明引數名的邊界
給指令碼檔案傳遞位置引數
script.sh:
#!/bin/bash
echo "Language: $1"
echo "Name: $2"
$ source script.sh Shell LiXiao
Language: Shell
Name: LiXiao
給函式傳遞位置引數
$ function func() {
$ echo "Language: $1"
$ echo "Name: $2"
$ }
$ func C++ LiXiao
Language: C++
Name: LiXiao
字串
# 獲取字串長度
echo ${#string} # 6
# 字串拼接
str1="a"
str2="b"
str3=$str1$str2"c" # 只需將字串放在一起就能拼接
字串擷取
# 擷取範圍,從左邊開始計數
$ str=111a222b333a444 # a 是第 3 位(從 0 開始計數)
$ echo ${str:3:5} # 從第 3 位開始輸出 5 位
a222b
$ echo ${str:3} # 從第 3 位開始輸出到末尾
a222b333a444
# 從右邊開始計數
$ echo ${str:0-4:5} # 從倒數第 4 位開始輸出 4 位。b 是倒數第 4 位(從 1 開始計數)
b333a
# 截去子串
# 截去左邊(截去字首)
$ echo ${str#*a} # 從左邊截去第一個匹配 a 及之前的所有字元
222b333a444
$ echo ${str#111a} # 省略萬用字元 *,此時需要寫出完整的字首
222b333a444
$ echo ${str##*a} # 從右邊截去第一個匹配 a 及之前的所有字元
444
# 截去右邊(截去字尾)
$ echo ${str%a*} # 從右邊截去第一個匹配 a 及之後的所有字元
111a222b333
$ echo ${str%%a*} # 從左邊截去第一個匹配 a 及之後的所有字元
111
陣列
陣列的宣告與使用
在 Zsh 中,陣列索引從 1 開始計數;在 Bash 中,陣列索引從 0 開始計數。
在 Shell 中,用 ()
來表示陣列,陣列元素之間用空格分隔:
# 陣列沒有長度限制
nums=(1 2 3) # 定義一個陣列
nums=(1 2 3 "abc") # 陣列沒有型別限制
nums+=(4 5 6) # 在陣列末尾新增元素
nums[20]=88 # 可以只給特定元素賦值
nums=([3]=24 [5]=19 [10]=12)
# 獲取陣列元素
echo ${nums[0]} # 輸出第 0 個元素:1(Bash)
echo ${nums[*]} # 輸出陣列所有元素
echo ${nums[@]} # 另一種寫法
unset nums[0] # 刪除陣列元素
unset nums # 刪除陣列
⚠️ Zsh 中陣列的行為與 Bash 有所不同:
array=(1 2 3)
echo $array # Bash:輸出第 0 個元素;Zsh:輸出所有元素
echo ${array[1]} # Bash:1;Zsh:2
echo $array[1] # 只有 Zsh 可以省略 {}
獲取陣列長度
和獲取字串長度的方法類似,使用 #
號:
# 兩種形式是等價的
echo ${#nums[*]}
echo ${#nums[@]}
陣列拼接
先將陣列展開,然後再合併到一起:
# 兩種形式是等價的
arr=(${nums_1[*]} ${nums_2[*]})
arr=(${nums_1[@]} ${nums_2[@]})
關聯陣列
也即 Python 中的字典
定義與使用
declare -A color # 定義一個名為 color 的關聯陣列
color["red"]="#ff0000"
color["green"]="#00ff00"
color["blue"]="#0000ff"
# 在定義的同時賦值
declare -A color=(
["red"]="#ff0000"
["green"]="#00ff00"
["blue"]="#0000ff"
)
echo $(color["red"]) # 訪問關聯陣列
操作關聯陣列
Bash
# 獲取所有鍵
# 兩種形式是等價的
${!color[*]}
${!color[@]}
Zsh
# 獲取所有鍵
${(k)color}
"${(@k)color}" # @ 使得每個鍵作為獨立個體存在,這種用法最常用
# 遍歷所有鍵和值
for key val in ${(kv)color}; do
echo "$key: $val"
done
數學運算
除了 C 語言中包含的所有算數運算子外,Shell 還包括冪運算子:**
整數運算
將表示式寫在雙小括號之間:((1+2))
多個表示式用 ,
分隔,最後一個表示式的值將作為命令的執行結果。
使用 $
獲取 (( ))
命令的結果。
Shell 中還有
let
命令,$[]
命令,它們的功能和(())
一樣,都是計算整數。 除此之外,還有expr
命令也可以用於整數計算。
e.g.
$ ((a=2-1))
$ ((b=a+4))
$ echo $((1+2, 2**3))
8
$ echo $((a<2&&b==5))
1
$ echo $((a++))
1
浮點數運算
使用 bc
命令
一行一個表示式,或者一行中多個表示式用分號 ;
分開。
quit
退出
C 語言中文網:Linux bc 命令
內建變數
變數名 | 作用 |
---|---|
scale |
指定精度,預設為 0 |
ibase |
指定輸入進位制 |
obase |
指定輸出進位制 |
last 或 . |
表示最近列印的數字 |
💡 Tip: obase 要儘量放在 ibase 前面,因為指定 ibase 後,所有後續輸入的數字都是以 ibase 的進位制來換算的。
內建函式
在輸入 bc
命令時需要使用 -l
選項啟用數學庫。
函式名 | 作用 |
---|---|
s(x) |
正弦函式,x 是弧度值 |
c(x) |
餘弦函式,x 是弧度值 |
a(x) |
反正切函式,返回弧度值 |
l(x) |
自然對數函式 |
e(x) |
自然指數函式 |
j(n,x) |
貝塞爾函式,計算從 n 到 x 的階數 |
在 Shell 中使用 bc 計算器
echo "expression" | bc # expression 可以包含 Shell 中的變數:$var
var=$(echo "expression" | bc)
e.g. 進位制轉換:
# 10 進位制轉 16 進位制
i=15
j=$(echo "obase=16;$i" | bc)
echo $j
# 16 進位制轉 10 進位制
k=$(echo "obase=10;ibase=16;$j" | bc) # 別忘了 obase 要寫在 ibase 前面
echo $k
藉助重定向使用 bc 計算器
var=$(bc << EOF # 分界符(EOF)可以自定義
2*3
last/2
EOF
這種方式在有大量數學計算時比較方便
declare 命令
語法:
declare [-/+] [選項] expression
declare 和 typeset 都是 Shell 內建命令,它們用法相同,不過 typeset 已經棄用。
-
表示設定屬性,+
表示取消屬性。
選項 | 含義 |
---|---|
-f | 列出之前由使用者在指令碼中定義的函式名稱和函式體 |
-F | 僅列出自定義函式名稱 |
-g | 在 Shell 函式內部建立全域性變數 |
-p | 顯示指定變數的屬性和值 |
-a | 宣告變數為普通陣列 |
-A | 宣告變數為關聯陣列 |
-i | 將變數定義為整型 |
-r | 將變數定義為只讀 |
-x | 將變數設定為環境變數 |
e.g.
$ declare -i n m num # 將 n, m, num 宣告為整型
$ declare -r CONST=23 # 將 CONST 宣告為只讀,等價於 readonly CONST
$ declare -p CONST # 顯示變數的屬性和值
declare -r CONST="23"
重定向
檔案描述符
為了表示並區分已經開啟的檔案,Linux 會給每個檔案分配一個 ID,即檔案描述符(File Descriptor)。stdin,stdout,stderr 是預設開啟的,它們已經有了自己的檔案描述符:
FD | 檔名 |
---|---|
0 | stdin |
1 | stdout |
2 | stderr |
Linux 始終從檔案描述符 0 讀取內容,向檔案描述符 1 輸出正確結果,向檔案描述符 2 輸出錯誤提示。
檔案描述符運算子(>
, <
)可以修改檔案描述符指向的檔案,從而實現重定向的功能。
輸出重定向
格式:FD>file
在輸出重定向中,>
表示覆蓋,>>
表示追加。
💡 Tip:
FD
和>
之間不能有空格,否則 Shell 會解析失敗;>
和file
之間的空格可有可無。
command >>file # 以追加輸出的方式開啟 file,並讓檔案描述符 1 指向 file。這裡 1 被省略
command 2>>file
command >>file1 2>>file2 # 正確結果輸出到 file1,錯誤提示輸出到 file2
command >>file 2>&1 # 2>&1:讓檔案描述符 2 指向檔案描述符 1 所指的檔案。結果是 1 和 2 都指向 file
command &>file # 以覆寫輸出的方式開啟 file,並讓檔案描述符 1 和 2 都指向 file
command n>&- # 關閉檔案描述符 n 及其代表的檔案。n 預設為 1
建議將正確結果和錯誤輸出儲存到不同檔案
/dev/null 檔案
如果不想顯示或儲存任何輸出,可以將輸出重定向到 /dev/null
檔案中:
ls > /dev/null # 僅拋棄標準輸出
ls 2> /dev/null # 僅拋棄錯誤輸出
ls &> /dev/null # 拋棄所有輸出
dir > NUL # CMD,僅拋棄標準輸出
dir 2>NUL # CMD,僅拋棄錯誤輸出
dir > NUL 2>&1 # CMD,拋棄所有輸出;2>&1 用於將標準錯誤重定向到標準輸出,從而同時拋棄標準輸出和標準錯誤。
Get-ChildItem | Out-Null # PowerShell,僅拋棄標準輸出
Get-ChildItem > $null # PowerShell,僅拋棄標準輸出
Get-ChildItem 2> $null # PowerShell,僅拋棄錯誤輸出
Get-ChildItem *> $null # PowerShell,拋棄所有輸出
輸入重定向
格式:FD<file
command <file # 以輸入的方式開啟 file,並讓檔案描述符 0 指向 file。這裡 0 被省略
command <input >output # 從 input 輸入,正確結果輸出到 output
command n<>file # 同時以輸入和輸出的方式開啟 file,並讓檔案描述符 n 指向 file。n 預設為 0
# 使用 Here Document
command <<EOF
document
EOF
Here Document
語法:
command <<END # END 是分界符,可以自定義
document
END # 行內不能有其他內容
Shell 將把 document 的內容輸入到命令中。
忽略命令替換
預設情況下,document 中出現的變數和命令也會被求值或執行。你可以將分界符用單引號或雙引號包圍來使 Shell 替換失效:
command <<'END'
document
END
忽略製表符
預設情況下,行首的製表符也被當做正文的一部分。不過我們輸入製表符僅僅是為了格式對齊,並不希望它作為正文的一部分。為了去掉製表符,你可以在 <<
和 END
之間新增 -
:
cat <<-END
document
END
常見用法
Here Document 最常用的功能還是向使用者顯示命令或者指令碼的用法資訊:
usage(){
cat <<-END
usage: command [-x] [-v] [-z] [file ...]
A short explanation of the operation goes here.
It might be a few lines long, but shouldn't be excessive.
END
}
程式碼塊重定向
將重定向命令放在程式碼塊的結尾處,就可以對程式碼塊中的所有命令實施重定向:
sum=0
while read n; do
((sum += n))
done <input >output # 輸入重定向為 input,輸出重定向為 output 。
# 組命令重定向
{
echo "line 1"
echo "line 2"
echo "line 3"
} >file
管道
command1 | command2 # 管道符 | 左邊命令的 stdout 將連線到右邊命令的 stdin
注意管道只處理正確的輸出(
stdout
),如果想讓管道也處理錯誤輸出(stderr
),需要將stderr
重定向到stdout
:command1 2>&1 | command2
curl -sSL https://install.python-poetry.org | python3 - # 從網路獲取安裝指令碼,然後執行
python3 -
表示 Python 將從管道讀取輸入
Runoob: Shell 中的特殊字元
管道與輸入重定向
command1 <input | command2 | command3 >output # 第一個命令從 input 獲取輸入,最後一個命令向 output 寫入輸出。
C 語言中文網:管道詳解
過濾器
過濾器:從標準輸入讀取資料,向標準輸出輸出結果的命令
常用過濾器:
命令 | 說明 |
---|---|
awk |
用於文字處理的解釋性程式設計語言,通常被作為資料提取和報告的工具。 |
cut |
用於將每個輸入檔案(如果沒有指定檔案則為標準輸入)的每行的指定部分輸出到標準輸出。 |
grep |
用於搜尋一個或多個檔案中匹配指定模式的行。 |
tar |
用於歸檔檔案的應用程式。 |
head |
用於讀取檔案的開頭部分(預設是 10 行)。如果沒有指定檔案,則從標準輸入讀取。 |
paste |
用於合併檔案的行。 |
sed |
用於過濾和轉換文字的流編輯器。 |
sort |
用於對文字檔案的行進行排序。 |
split |
用於將檔案分割成塊。 |
strings |
用於列印檔案中可列印的字串。 |
tac |
與 cat 命令的功能相反,用於倒序地顯示檔案或連線檔案。 |
tail |
用於顯示檔案的結尾部分。 |
tee |
用於從標準輸入讀取內容並寫入到標準輸出和檔案。 |
tr |
用於轉換或刪除字元。 |
uniq |
用於報告或忽略重複的行。 |
wc |
用於列印檔案中的總行數、單詞數或位元組數。 |
grep
grep 'word' file # 在 file 中查詢並顯示包含 word 的行
grep -i 'word' file # 忽略大小寫
grep -R 'word' . # 在當前目錄及其子目錄下的所有檔案中查詢並顯示包含 word 的行
grep -c 'word' file # 搜尋並顯示 word 在 file 中出現的次數
Shell 結構語句
if else
if condition # 也可以寫作:if condition; then
then
statements
else
statements
fi
💡 Tip: condition 檢測的是命令的退出狀態。通常退出狀態為 0 表示 “成功” ,其他狀態表示 “失敗” 。
((1))
的退出狀態為 0 。
if elif else
if condition1; then
statements
elif condition2; then
statements
elif condition3; then
statements
else
statements
fi
if 語句也支援使用邏輯運算子將多個退出狀態組合起來。
case in
語法:
case expression in # expression 可以是變數、數學表示式,或者是命令的執行結果。只要能得到 expression 的值就可以
pattern1) # pattern 可以是數字,字串,甚至是簡單的正規表示式
statements
;; # 兩個分號表示一條 case 語句的結束
pattern2)
statements
;;
pattern3)
statements
;;
*) # 可以沒有 *) 部分。* 實際上是正規表示式
statements # 最後的雙分號可以省略
esac
case in 支援的正規表示式
格式 | 說明 |
---|---|
* |
表示任意字串 |
[abc] |
表示 a, b, c 三個字元中的任意一個。比如 [15ZH] 表示 1, 5, Z, H 中的任意一個 |
[m-n] |
表示從 m 到 n 的任意一個字元。比如 [0-9] 表示一個數字,[0-9a-zA-Z] 表示一個字母或數字 |
| |
表示多重選擇,相當於或運算。比如 abc|xyz 表示匹配字串 "abc" 或 "xyz" |
e.g.
read -n 1 char
case $char in
[a-zA-Z])
printf "\n字母\n"
;;
[0-9])
printf "\n數字\n"
;;
[,.?!])
printf "\n標點符號\n"
;;
*)
printf "\n錯誤\n"
esac
select in
select in
可以自動顯示帶編號的選單,使用者輸入編號就可以為迴圈變數賦予編號對應的值。select in
經常與 case in
一起使用。
語法(結合 case in
):
select variable in value_list
do
case variable in
pattern1)
statements
break # select in 只有遇到 break 或檔案結束符才會退出迴圈
;;
pattern2)
statements
break
;;
pattern3)
statements
break
;;
*)
echo "輸入錯誤,請重新輸入。"
esac
done
可以修改環境變數
PS3
來自定義輸入提示符
break,continue
break n
continue n
其中 n 表示作用的迴圈的層數(從內向外)。
while
語法:
while condition
do
statements
done
while true; do
statements
done
until
until 的使用場景很少,一般使用 while 即可。
語法:
until condition # 若 condition 不成立則執行迴圈語句
do
statements
done
for
C 風格 for
sum=0
for((i=1; i<=5; ++i))
do
((sum += i))
done
echo $sum
Python 風格 for in
for variable in value_list
do
statements
done
value_list
- 直接給出具體的值:
1 2 3 4 5
,"john" "jack" "tom"
- 給出一個取值範圍(只支援數字和字母):
{1..5}
,{a..f}
- 使用命令的執行結果:
$(ls)
- 使用 Shell 萬用字元:
*.sh
- 使用特殊變數:
$@
(如果省略 value_list 的話相當於使用 $@)
Shell 萬用字元
[[#特殊變數]]
函式
語法:
# 定義
function func() { # function 關鍵字可以省略。如果寫了 function 關鍵字,那麼 () 可以省略
statements
[return value]
}
function func { # 省略 ()
...
}
func() { # 省略 function
...
}
function func --description "some description" # 你可以為函式新增描述
# 呼叫
func [param1] [param2] [...] # 函式名後面不加括號
Shell 不限制定義和呼叫的順序。
函式返回值
Shell 中的函式返回值是一個介於 0~255 的整數,用來表示函式的退出狀態:返回值為 0 表示函式執行成功,非 0 表示函式執行失敗。函式執行失敗時可以根據退出狀態來判斷具體出現了什麼錯誤。
💡 Tip: 在 Shell 中不要用返回值表示函式的處理結果
如果函式體中沒有 return 語句,那麼函式將使用最後一條命令的退出狀態作為自己的返回值。
如何得到函式的處理結果
要想得到函式的處理結果,可以使用以下兩種方案:
- 在函式內部使用
echo
,printf
等命令將結果輸出,並在函式外部使用$()
或``
捕獲結果。(推薦) - 將處理結果賦給全域性變數
條件表示式
[[ ]]
用來檢測某個條件是否成立。
[[ ]]
是 Shell 內建關鍵字,不是命令,在使用時沒有給函式傳遞引數的過程。因此相比 test 命令,[[ ]]
不需要關注某些細枝末節:
- 不需要把變數名用雙引號
""
包圍起來。即使變數是空值,也不會出錯。 - 不需要對
>
、<
進行轉義。
語法:
[[ expression ]] # 注意兩側的空格
檔案檢測運算子
expression | 作用 |
---|---|
-e path |
判斷 path 是否存在 |
-d path |
判斷 path 是否存在,並且是否為目錄檔案 |
-f path |
判斷 path 是否存在,並且是否為普通檔案 |
-s path |
判斷 path 是否存在,並且是否非空 |
-r path |
判斷 path 是否存在,並且是否擁有讀許可權 |
-w path |
判斷 path 是否存在,並且是否擁有讀許可權 |
-x path |
判斷 path 是否存在,並且是否擁有執行許可權 |
path1 -nt path2 |
判斷 path1 的修改時間是否比 path2 新 |
path1 -ot path2 |
判斷 path1 的修改時間是否比 path2 舊 |
read fileName
read msg
if [[ -w $fileName ]] && [[ -n $msg ]]; then
echo $msg >$fileName
echo "寫入成功"
else
echo "寫入失敗"
fi
使用 !
的情況:
if [[ ! -e $path ]]; then
echo "$path 不存在"
fi
# 不要寫成下面這樣
if ![[ -e $path ]]
# 或者
if [[ !(-e $path) ]]
字串判斷運算子
expression | 作用 |
---|---|
-z str |
判斷 str 是否為空 |
-n str |
判斷 str 是否非空 |
str1 = str2 str1 == str2 |
判斷 str1 是否和 str2 相等 |
str1 != str2 |
判斷 str1 是否和 str2 不相等 |
str1 > str2 |
判斷 str1 是否大於 str2 |
str1 < str2 |
判斷 str1 是否小於 str2 |
💡注意:在使用
=
、==
、!=
、>
、<
運算子比較字串時,運算子與兩邊的字串之間一定要有空格!否則可能會出現奇葩問題。
read str1
read str2
# 檢測字串是否為空
# 條件表示式支援邏輯運算子
if [[ -z $str1 || -z $str2 ]]; then
echo "字串不能為空"
# 比較字串
elif [[ $str1 < $str2 ]]; then
echo "str1 < str2"
else
echo "str1 >= str2"
fi
條件表示式支援正規表示式
可以使用 =~
來檢測字串是否符合某個正規表示式:
# 檢測一個字串是否是手機號
read tel
if [[ $tel =~ ^1[0-9]{10}$ ]]; then
echo "你輸入了一個手機號"
else
echo "你輸入的不是手機號"
fi
更多運算子請參見 C 語言中文網
echo 輸出彩色字元
echo -e "\033[背景色;前景色m輸出文字"
echo -e "\e[31m輸出文字" # 只指定前景色,輸出紅色字元
echo -e "\e[41m輸出文字" # 只指定背景色,輸出紅底白字
echo -e "\e[0m輸出文字" # 使用預設配色
# 樣例
echo -e "\e[31mHello, World\e[0m" # 紅色 Hello, World
# 注意,在 -e 選項的 echo 命令中,輸出語句如果含有感嘆號 !,則感嘆號的後面只能是空白符或語句結束的雙引號。否則 ! 會被解析成事件提示符
echo -e "文字"'!'"文字" # 解決 echo -e 輸出 ! 的問題
# 設定顏色變數
GREEN="\e[32m"
RES="\e[0m"
echo -e "${GREEN}Hello, World$RES"
# 設定顏色動作
PRT_GREEN="echo -e \e[32m"
${SET_GREEN}"message"$RES
背景色 | 前景色 | 顏色 |
---|---|---|
40 | 30 | 黑色 |
41 | 31 | 紅色 |
42 | 32 | 綠色 |
43 | 33 | 黃色 |
44 | 34 | 紫色 |
45 | 35 | 粉色 |
46 | 36 | 藍色 |
47 | 37 | 灰色 |
\033[背景色;前景色m
是轉義序列,其中\033[
是轉義起始符,m
是轉義終止符。
\033
對應 ASCII 碼錶的 Esc,可以用\e
或\E
代替。背景色和前景色沒有先後順序。適應我們慣常的思維順序,一般先確定背景色,再確定前景色。
C 語言中文網:echo 命令:顯示文字並給文字新增顏色
事件提示符
!100 # 執行第 100 條命令
!-1 # 執行倒數第一條命令
!! # !-1 的 alias
!abc # 引用最近的以 abc 開頭的命令。可以在執行一條命令之後忘記這個命令的引數時使用。
!?abc # 引用最近的包含 abc 的命令
# 間接取值
$ a="Hello"
$ b="a"
$ echo ${!b}
Hello
在 Shell 中,使用 ⬆️ 和 ⬇️ 也可以快速切換上一條、下一條命令。
Linux 多命令順序執行連線符
| 符號 | 作用 |
| :--: | ------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| ;
| 沒有任何邏輯關係的連線符。當多個命令用分號連線時,各命令之間的執行成功與否彼此沒有任何影響,都會一條一條執行下去。 |
| | |
| 邏輯或,當用此連線符連線多個命令時,前面的命令執行成功,則後面的命令不會執行。前面的命令執行失敗,後面的命令才會執行。 |
| &&
| 邏輯與,當用此連線符連線多個命令時,前面的命令執行成功,才會執行後面的命令,前面的命令執行失敗,後面的命令不會執行 |
| |
| 管道符,當用此連線符連線多個命令時,前面命令執行的正確輸出,會交給後面的命令繼續處理。若前面的命令執行失敗,則會報錯,若後面的命令無法處理前面命令的輸出,也會報錯。 |
查詢歷史記錄
history
對於 Zsh 使用者,可以使用 fc
命令來搜尋歷史記錄,它是 history
命令的增強版本:
fc -l 1
這裡 -l
選項意味著列出歷史條目,1
是從歷史記錄的第一條命令開始搜尋。
最後,如果你在 Zsh 中啟用了增強的歷史搜尋功能,也可以直接透過按下特定的快捷鍵(如 Ctrl+R)進入增量式搜尋模式。例如:
- 在命令列提示符下按下 Ctrl + R
- 然後開始輸入你要搜尋的命令片段,如 cd
(reverse-i-search)`cd': cd /path/to/directory
在增量式搜尋模式下,你可以繼續輸入來精確搜尋,或者按下 Ctrl + R 來檢視匹配項的上一個條目。按下回車即可執行搜尋結果中的命令,或按下 Esc 或者 Ctrl + G 來退出搜尋模式而不執行任何命令。