Shell
(Unix Shell
)是一種命令行解釋器,是Unix
操作系統下最傳統的人機接口。
Shell
腳本是解釋執行的,不需要編譯,和大部分的編程語言很相似,也有基本的變量和流程控制語句。
平時使用
Shell
有兩種方式:
- 輸入命令,執行,這種方式稱為交互式(
Interactive
);- 批處理(
Batch
)方式,用戶事先寫好Shell
腳本文件,然后順序執行腳本中的命令。
第一個
Shell
環境是Thompson Shell
,在貝爾實驗室開發并于1971年
發布現代
Shell
最突出的祖先是被稱為sh
的BourneShell
,這是以在AT&T
工作的創始人Stephen Bourne
命名的
Shell
一直在基于這個概念,不斷添加各種新功能,演變出很多種的Shell
例如:很早版本的
OS X
中使用的是:
tcsh
作為默認的Shell
。這是由csh(C shell)
,一種類似C
語言的Shell
演變而來在
OS X 10.3
版與10.4
版之后,默認的Shell
是:
bash
,由GNU
開發除了默認的
bash
,現在macOS
中,默認的Shell
變成了zsh
這是一種由
Paul Falstad
于1990年
開發的。它是一個Bourne
式Shell
,它使用bash
和previous shell
的特性,并添加了更多的特性:
- 拼寫檢查功能
- 內置的編程特性
- 友好的交互
與此同時,
macOS
還提供了很多其他種類的Shell
:ls -ls /bin/*sh ------------------------- 680 -r-xr-xr-x 1 root wheel 623472 8 11 2020 /bin/bash 520 -rwxr-xr-x 1 root wheel 529424 8 11 2020 /bin/csh 112 -rwxr-xr-x 1 root wheel 110848 8 11 2020 /bin/dash 1440 -r-xr-xr-x 1 root wheel 1300256 8 11 2020 /bin/ksh 16 -rwxr-xr-x 1 root wheel 31440 8 11 2020 /bin/sh 520 -rwxr-xr-x 1 root wheel 529424 8 11 2020 /bin/tcsh 736 -rwxr-xr-x 1 root wheel 637840 8 11 2020 /bin/zsh
.bashrc、.bash_profile和.zshrc作用與區別
在使用命令行工具時,我們可能會遇到一些教程,可能需要你把一些配置寫入到.bashrc
、.bash_profile
或者.zshrc
等。那么這幾個文件到底有什么作用和區別?
從文件名稱判斷
.bashrc
、.bash_profile
是給bash
來使用的。而.zshrc
是給zsh
來使用的查看
bash
版本:bash ------------------------- The default interactive shell is now zsh. To update your account to use zsh, please run `chsh -s /bin/zsh`. For more details, please visit https://support.apple.com/kb/HT208050. bash-3.2$
查看
zhs
的安裝路徑:which zsh ------------------------- /bin/zsh
查看
bash
的安裝路徑:which bash ------------------------- /bin/bash
zsh
切換bash
,重新打開終端即可chsh -s /bin/bash
bash
切換zsh
,重新打開終端即可chsh -s /bin/zsh
交互式登錄和非登錄Shell
當調用
Shell
時,Shell
從一組啟動文件中讀取信息并執行命令。讀取什么文件就取決于Shell
是作為交互式登錄還是非登錄調用。換言之,
Shell
分為交互式的或非交互式的:
- 交互式
Shell
是讀取和寫入到用戶終端的Shell
程序,用戶在終端上輸入命令,并在回車后立即執行- 非交互式
Shell
是與終端不相關的Shell
程序。例如:執行腳本時交互式
Shell
可以是登錄Shell
,也可以是非登錄Shell
當用戶通過
ssh
或本地遠程登錄到終端時,或者使用--login
選項啟動時,將調用登錄Shell
zsh --login
當
bash
作為交互式登錄Shell
調用時,bash
會先查找/etc/profile
文件,如果該文件存在,它將運行文件中列出的命令,然后搜索~/.bash_profile
、~/.bash_login
以及~/.profile
文件,順序讀取當
bash
作為交互式非登錄Shell
調用時,會讀取~/.bashrc
cat /etc/profile ------------------------- # System-wide .profile for sh(1) if [ -x /usr/libexec/path_helper ]; then eval `/usr/libexec/path_helper -s` fi if [ "${BASH-no}" != "no" ]; then [ -r /etc/bashrc ] && . /etc/bashrc fi
所以說,
.bashrc
和.bash_profile
之間的區別是,.bash_profile
當bash
作為交互式登錄Shell
調用時被讀取并執行,而.bashrc
對于交互式非登錄Shell
被執行
大多數
Linux/Unix
發行版都使用~/.profile
代替~/.bash_profile
。~/.profile
所有Shell
都讀取該文件,而~/.bash_profile
只有bash
才會讀取該文件
~/.zshrc
是zsh
的交互式Shell
的用戶配置
對于
bash
,它們的工作方式如下:
讀取適當的內容,執行
A
,然后執行B
,然后執行C
,依此類推。B1
,B2
,B3
表示僅執行找到的那些文件中的第一個。+----------------+-----------+-----------+------+ | |Interactive|Interactive|Script| | |login |non-login | | +----------------+-----------+-----------+------+ |/etc/profile | A | | | +----------------+-----------+-----------+------+ |/etc/bash.bashrc| | A | | +----------------+-----------+-----------+------+ |~/.bashrc | | B | | +----------------+-----------+-----------+------+ |~/.bash_profile | B1 | | | +----------------+-----------+-----------+------+ |~/.bash_login | B2 | | | +----------------+-----------+-----------+------+ |~/.profile | B3 | | | +----------------+-----------+-----------+------+ |BASH_ENV | | | A | +----------------+-----------+-----------+------+ | | | | | +----------------+-----------+-----------+------+ | | | | | +----------------+-----------+-----------+------+ |~/.bash_logout | C | | | +----------------+-----------+-----------+------+
對于
zsh
,它們的工作方式如下:讀取適當的內容,執行
A
,然后執行B
,然后執行C
,依此類推。+----------------+-----------+-----------+------+ | |Interactive|Interactive|Script| | |login |non-login | | +----------------+-----------+-----------+------+ |/etc/zshenv | A | A | A | +----------------+-----------+-----------+------+ |~/.zshenv | B | B | B | +----------------+-----------+-----------+------+ |/etc/zprofile | C | | | +----------------+-----------+-----------+------+ |~/.zprofile | D | | | +----------------+-----------+-----------+------+ |/etc/zshrc | E | C | | +----------------+-----------+-----------+------+ |~/.zshrc | F | D | | +----------------+-----------+-----------+------+ |/etc/zlogin | G | | | +----------------+-----------+-----------+------+ |~/.zlogin | H | | | +----------------+-----------+-----------+------+ | | | | | +----------------+-----------+-----------+------+ | | | | | +----------------+-----------+-----------+------+ |~/.zlogout | I | | | +----------------+-----------+-----------+------+ |/etc/zlogout | J | | | +----------------+-----------+-----------+------+
如何確認當前是登錄還是非登錄
Shell
?確認當前終端
tty
使用的Shell
類型:echo $0 ------------------------- zsh
- 前面有
-
,表示當前已登錄- 前面沒有
-
,表示當前非登錄使用
login
命令登錄login ------------------------- login: Zang Password:***
再次查看
echo $0 ------------------------- -zsh
配置建議
bash
:
- 將配置選項放到
~/.bashrc
中,然后在~/.bash_profile
中通過source
調用
zsh
:
- 建議仍然將配置選項放到
~/.bashrc
,~/.bash_profile
中通過source
調用,最后在~/.zshrc
中source
調用~/.bash_profile
編譯器與解釋器
編譯型語言需要編譯處理:
- 源代碼(
source code
)-> 預處理器(preprocessor
)-> 編譯器(compiler
)-> 目標代碼(object code
)-> 鏈接器(Linker
)-> 可執行程序(executables
)
解釋型語言需要解釋器處理:
- 源代碼(
source code
)-> 解釋器(interpreter
)
Shell初探
Shebang
(Hashbang
):一個由井號和嘆號構成的字符序列#!
出現在文本文件的第一行的前兩個字符#!/bin/bash
操作系統的程序加載器會分析
Shebang
后的內容,將這些內容作為解釋器指令。并調用該指令,并將載有Shebang
的文件路徑作為該解釋器的參數
env
:不同對操作系統,腳本解釋器可能被安裝于系統的不同的目錄,設置到系統的PATH
中
env
可以在系統的PATH
目錄中查找#!/usr/bin/python #!/usr/bin/env pyhon
- 上述命令,使用在用戶路徑中找到的第一個
Python
版本可以通過指定版本號:
#!/usr/bin/env pythonX.x
env
也可以指定搜索目錄:#!/usr/bin/env -S -P/usr/local/bin:/usr/bin:${PATH} python
- 在
/usr/local/bin
、/usr/bin
、系統PATH
搜索Python
Shell
子進程:是從父子進程的概念出發的,unix
操作系統的進程從init
進程開始(init
進程為1
,而進程號0
為系統原始進程,以下討論的進程原則上不包括進程0
)均有其對應的子進程,就算是由于父進程先行結束導致的孤兒進程,也會被init
領養,使其父進程ID
為1
因為所有的進程均有父進程,事實上,所有進程的創建,都可視為子進程創建過程。
unix
操作系統進程的創建,大致都是進行fork + exec
類系統調用理解子進程的創建執行,需要至少細分到二個步驟,包括:
- 通過
fork
創建子進程環境,- 通過
exec
加載并執行進程代碼。
Shell
子進程(以下均稱subshell
):顧名思義,就是在當前的Shell
下,去打開另一個新Shell
PS1
提示符定義:提示符設置[\u@\h \w \A #\#]\$
\u
:用戶賬號名稱\h
:主機名縮寫\w
:完整工作路徑\A
:24小時
時間\#
:第幾個命令\$
:提示符,如果是root
,提示符為#
,否則是$
HOME
:代表用戶主文件夾。
SHELL
:當前使用的是那個SHELL
PATH
:執行文件查找路徑查看
PS1
提示符:echo $PS1 ------------------------- %{%f%b%k%}$(build_prompt)
read [-pt]
:讀取鍵盤變量
-p
:提示符-t
:等待秒數寫入:
read website Cat
讀取:
echo $website ------------------------- Cat
注釋
單行注釋
# echo "單行注釋"
多行注釋
- 方式一:
: << ! 多行注釋方式一: echo "多行注釋" !
- 方式二:
: << COMMENT 多行注釋方式二: echo "多行注釋" COMMENT
- 方式三:
: ' 多行注釋方式三: echo "多行注釋" '
- 方式四:
if false; then 多行注釋方式四: echo "多行注釋" fi
- 方式五
((0)) && { 多行注釋方式五: echo "多行注釋" }
特殊符號的使用
雙引號:
""
首先,單引號和雙引號,都是為了解決中間有空格的問題。
因為空格在
Shell
中作為一個很典型的分隔符,比如string1=this is astring
,這樣執行就會報錯。為了避免這個問題,因此就產生了單引號和雙引號。他們的區別在于,單引號將剝奪其中的所有字符的特殊含義,而雙引號中的$(參數替換)
和反引號(命令替換與$()
作用一樣)是例外。所以,兩者基本上沒有什么區別,除非在內容中遇到了參數替換符$
和命令替換符反引號雙引號常用于包含一組字符串,在雙引號中,除了
$
、\
、`
(反引號)有特殊含義外,其余字符(如換行符、回車符等)沒有特殊含義
echo
輸出1&2
,由于&
是Shell
中的特殊符號,直接運行命令會報錯的echo 1&2 ------------------------- [1] 56375 1 cd: no such entry in dir stack [1] + 56375 done echo 1
可以使用雙引號
""
,以此解決可能會產生歧義的問題echo "1&2" ------------------------- 1&2
使用雙引號
""
進行多行輸入echo " 1&2 " ------------------------- 1&2
- 輸出時,前后都有回車。因為換行符、回車符在雙引號中沒有特殊含義,也會直接輸出
反引號:
``
反引號的功能是命令替換,在反引號
``
中的內容通常是命令行,程序會優先執行反引號中的內容,并使用運行結果替換掉反引號處的內容echo "I am `echo Cat`" ------------------------- I am Cat
- 在按照
Shell
從上往下,從左往右執行規則的前提下,優先執行反引號``
的內容
單引號:
''
單引號的功能與雙引號類似,不過單引號中的所有字符都沒有特殊含義
echo ' i am `echo Cat` ' ------------------------- i am `echo Cat`
- 反引號
``
在單引號''
中沒有生效,因為單引號中的所有字符都沒有特殊含義
$ + 小括號
:$()
作用與反引號
``
一樣,也是命令替換echo "i am $(echo Cat)" ------------------------- i am Cat
$ + 雙小括號
:$(())
$(())
的功能是進行算術運算,括號中的內容為數學表達式echo "20 + 5 * 6 = $((20 + 5 * 6))" ------------------------- 20 + 5 * 6 = 50
$((20 + 5 * 6))
返回雙括號內算數運算的結果
$ + 中括號
:$[]
$[]
的功能與$(())
一樣,都是用于算術運算echo "20 + 5 * 6 = $[20 + 5 * 6]" ------------------------- 20 + 5 * 6 = 50
$ + 大括號
:${}
${}
的功能是變量引用,類似于$
符,但是${}
比$
的替換范圍更精準
小括號:
()
用來定義一個數組變量
雙小括號:
(())
雙小括號命令允許在比較過程中使用高級數學表達式
中括號:
[]
單個的中括號的功能與
test
命令一樣,都是用作條件測試
雙中括號:
[[]]
雙中括號提供了針對字符串比較的高級特性,使用雙中括號
[[]]
進行字符串比較時,可以把右邊的項看做一個模式,故而可以在[[]]
中使用正則表達式
大括號:
{}
大括號用于括起一個語句塊
冒號:
(:)
作為內建命令:占位符、參數擴展和重定向
Shell
里面的括號
${a}
:變量a
的值, 在不引起歧義的情況下可以省略大括號$(cmd)
:命令替換, 和cmd
效果相同$((exp))
:增強括號的用法和expr exp
效果相同,計算數學表達式exp
的數值。其中exp
只要符合C語言
的運算規則即可,甚至三目運算符和邏輯表達式都可以計算。可以省略$
例如:
for((i=0;i<5;i++)) # 如果不使用雙括號 for i in seq 0 4 for i in {0..4}
if (($i<5)) # 如果不使用雙括號 if [ $i -lt 5 ]
expr
命令:是一款表達式計算工具,使用它完成表達式的求值操作
expr 1 + 2 + 3 - 4 ------------------------- 2
語法要求,中間的空格必須加
eval
命令:對后面的
cmdLine
進行兩遍掃描,如果第一遍掃描后,cmdLine
是個普通命令,則執行此命令;如果cmdLine
中含有變量的間接引用,則保證間接引用的語義
type
命令:顯示命令屬性,會顯示該命令所在的位置
type [-aftpP] name [name,...]
-a
:打印name
的所有可能情況-f
:不會去查找function
-t
:打印alias
、keyword
、function
、built-in
、file
這五種類型-p
:如果type -t name
輸出file
,那么會打印name
所在路徑-P
:不管type -t name
是不是輸出file
,都會去搜索name
所在路徑。例如:type -P ls
,盡管type -t ls
打印的是alias
(因為alias
的優先級高于file
),但是仍然會搜索出ls
所在的路徑/bin/ls
type -a ls ------------------------- ls is an alias for ls -G ls is /bin/ls
如果
type
不加任何選項,直接加一個或者多個name
,那么會依次打印這些name
的類型。只有所有name
的類型都能成功打印,type
才返回成功。否則,只要任何一個name
類型無法打印,那么就返回失敗
echo
命令:按照規則打印字符串
echo [-ne][字符串]
-n
:不要在最后自動換行-e
:若字符串中出現以下字符,則特別加以處理:\a
:發出警告;\b
:刪除前一個字符;\c
:不產生進一步輸出(\c
后面的字符不會輸出);\f
:換行但光標仍舊停留在原來的位置;\n
:換行且光標移至行首;\r
:光標移至行首,但不換行;\t
:插入tab
;\v
:與\f
相同;\\
:插入\
字符;\nnn
:插入nnn
(八進制)所代表的ASCII
字符;用
echo
命令打印帶有色彩的文字:
- 文字色:
echo -e "\e[1;31mThis is red text\e[0m"
\e[1;31m
:將顏色設置為紅色
\e[0m
:將顏色重新置回
顏色碼:重置=0
,黑色=30
,紅色=31
,綠色=32
,黃色=33
,藍色=34
,洋紅=35
,青色=36
,白色=37
- 背景色:
echo -e "\e[1;42mGreed Background\e[0m"
顏色碼:重置=0
,黑色=40
,紅色=41
,綠色=42
,黃色=43
,藍色=44
,洋紅=45
,青色=46
,白色=47
- 文字閃動:
echo -e "\033[37;31;5mLogic Cat 帥帥帥...\033[39;49;0m"
紅色數字處還有其他數字參數:0
關閉所有屬性、1
設置高亮度(加粗)、4
下劃線、5
閃爍、7
反顯、8
消隱
grep
命令:用于查找文件里符合條件的字符串
創建
Common Symbol
目錄,Common Symbol
目錄下創建test.m
文件,寫入以下代碼:#import <Foundation/Foundation.h> void SomeNewFunction_weak_import(void) __attribute__((weak_import)); void SomeNewFunction_weak_import(void) { NSLog(@"SomeNewFunction_weak_import"); } void SomeNewFunction_weak(void) __attribute__((weak)); void SomeNewFunction_weak(void) { NSLog(@"SomeNewFunction_weak"); }
在
test.m
文件中,搜索weak
字符串
grep "$KEY_WORD" --color=auto -- $FILE
grep weak --color=auto -- ../Common\ Symbol/test.m ------------------------- void SomeNewFunction_weak_import(void) __attribute__((weak_import)); void SomeNewFunction_weak_import(void) { NSLog(@"SomeNewFunction_weak_import"); void SomeNewFunction_weak(void) __attribute__((weak)); void SomeNewFunction_weak(void) { NSLog(@"SomeNewFunction_weak");
-- ../Common\ Symbol/test.m
:其中--
表示當前命令已經沒有參數要添加了,后面的內容都是傳遞給命令的- 可以通過
--
的方式,將內容和參數區分開路徑中包含空格,則有三種不同的解決策略:
- 加
\
轉義grep weak --color=auto -- ../Common\ Symbol/test.m
- 加雙引號
grep weak --color=auto -- "../Common Symbol/test.m"
- 加單引號
grep weak --color=auto -- '../Common Symbol/test.m'
錯誤寫法:
grep weak --color=auto -- "../Common\ Symbol/test.m" grep weak --color=auto -- '../Common\ Symbol/test.m' ------------------------- grep: ../Common\ Symbol/test.m: No such file or directory
- 使用雙引號或單引號,里面不能再加
\
轉義
標準輸出 & 輸入 & 錯誤輸出
標準輸出和標準錯誤輸出可以將內容重定向輸出到指定的設備(如打印機)或文件中,標準輸入可以使用文件或其他輸入替換手動輸入。
標準輸出(
stdout
):代碼為1
,使用>
或>>
使用
>
或1>
以覆蓋的方式,將正確的數據輸出到指定到文件或設備echo "LGCat" > Cat echo "LGCat123" > Cat
- 目錄下生成
Cat
文件,只會記錄最后輸出的LGCat123
使用
>>
或1>>
以累加到方式,將正確到數據輸出到指定到文件或者設備上echo "LGCat" >> Cat1 echo "LGCat123" >> Cat1
- 目錄下生成
Cat1
文件,將兩次輸出內容累加記錄
標準錯誤輸出(
stderr
):代碼為2
,使用2>
或2>>
使用
2>
以覆蓋的方式,將錯誤的數據輸出到指定到文件或設備echo "LGCat" 2> Cat
使用
2>>
以累加的方式,將錯誤的數據輸出到指定到文件或設備echo "LGCat" 2>> Cat1
標準輸入(
stdin
):代碼為0
,使用<
或<<
使用
<
標準輸入cat < Cat ------------------------- LGCat123
使用
cat > file
或cat >> file
標準輸入cat > Cat1237 111 222
- 使用
> file
等待輸入,將輸入的內容保存到file
中,使用control + d
結束輸入。>
覆蓋>>
累加使用
<<
結束輸入,后接一個結束輸入符cat > Cat1237 << "cat" 111 222 cat
- 將
cat
作為結結束提示符- 按照日常規范建議使用
eof
表示結束提示符
單箭頭和雙箭頭的區別:
對于輸出:
- 單箭頭:當指定的文件不存在時,創建新文件寫入數據;當文件存在時,清空原文件的內容寫入數據
- 雙箭頭:當指定的文件不存在時,創建新文件寫入數據;當文件存在時,在原件內容的最后追加寫入數據
對于輸入:
- 單箭頭:將文件或其他輸入作為標準輸入(
<
的左邊必須是命令,<
右邊的輸入內容作為命令的輸入)- 雙箭頭:結束輸入
將標準輸出和錯誤輸出重定向到一個文件上:
將
stdin
和stderr
無序輸出到one.log
:grep "Cat" file.log > one.log 2>one.log
將
stdin
和stderr
序有輸出到one.log
:grep "Cat" file.log > one.log 2>&1
- 首先
stdin
重定向到one.log
,然后使用2>&1
表示stderr
重定向至stdin
,stderr
在stdin
之后輸入到one.log
中簡寫:
grep "Cat" file.log &> one.log
使用
>/dev/null
將錯誤的數據丟棄,只顯示正確的數據echo "123" > /dev/null
/dev/null
代表linux
的空設備文件,所有往這個文件里面寫入的內容都會丟失,俗稱“黑洞”使用
&>
或者>&
將正確的數據和錯誤的數據寫入同一個文件echo "111" &> Cat echo "222" >& Cat
使用
1>&2
正確返回值傳遞給2
輸出通道,&2
表示2
輸出通道echo "111222" 1>&2
使用
2>&1
錯誤返回值傳遞給1
輸出通道,&1
表示1
輸出通道echo "111222" 2>&1
cmd;cmd
不考慮命令相關性,連續執行。echo "Cat1";echo "Cat2" ------------------------- Cat1 Cat2
當前一個命令執行成功會回傳一個
$?=0
的值e ------------------------- zsh: command not found: e ------------------------- echo $? ------------------------- 127
cmd1 && cmd2
如果第一個命令的$?
為0
,則執行第二個命令echo "Cat1" && echo "Cat2" ------------------------- Cat1 Cat2
cmd1 || cmd2
如果第一個命令的$?
為0
,則不執行第二個命令。否則執行第二個命令e || echo "Cat" ------------------------- zsh: command not found: e Cat
|
:管道僅能處理前面一個命令傳來的正確信息,將正確信息作為stdin
傳給下一個命令echo "test.m" | grep weak
- 管道命令只處理前一個命令正確輸出,不處理錯誤輸出
- 管道命令右邊命令,必須能夠接收標準輸入流命令才行
- 大多數命令都不接受標準輸入作為參數,只能直接在命令行輸入參數,這導致無法用管道命令傳遞參數
xargs
:是將標準輸入轉為命令行參數echo "Cat1237" | xargs cat ------------------------- 111 222
-
:減號在一些命令中表示從標準輸入(stdin
)中獲取內容echo "Cat1237" | xargs cat - ------------------------- 111 222
三種運行方式
sh
:
- 使用
$sh script.sh
執行腳本時,當前Shell
是父進程,生成一個子Shell
進程,在子Shell
中執行腳本- 腳本執行完畢,退出子
Shell
,回到當前Shell
。$./script.sh
與$sh script.sh
等效。也叫fork
方式
source
:
- 使用
$source script.sh
方式,在當前上下文中執行腳本,不會生成新的進程。腳本執行完畢,回到當前Shell
$sh script.sh
與$source script.sh
等效。不需要有"執行權限"
exec
:
- 使用
exec ./scriptsh
方式,會用command
進程替換當前Shell
進程,并且保持pid
不變- 執行完畢,直接退出,不回到之前的
Shell
環境
執行權限
sh/bash/zsh
:不需要執行權限./script.sh
:需要執行權限source script.sh
:不需要執行權限exec
:需要執行權限
變量的定義
Shell
變量默認為字符串。Shell
不關心這個串是什么含義declare a=3 b=4 c c=a+b echo $c ------------------------- a+b
Shell
默認的數值運算是整數類型。所以若要進行數學運算,必須使用一些命令,例如:declare
、expr
、雙括號等declare a=`expr 3 + 4` b=$((1+1)) echo $a;echo $b ------------------------- 7 2
Shell
變量可分為兩類:
- 局部變量:只在創建它們的
Shell
中可用。在函數內定義,函數執行后就被刪除- 環境變量:可以在創建它們的
Shell
及其派生出來的任意子進程中使用。在整個腳本執行期間,只要沒有被刪除就一直存在export a=3 echo $a ------------------------- 3
定義規則:
- 變量名必須以字母或下劃線字符開頭,其余的字符可以是字母、數字(
0~9
)或下劃線字符。任何其他的字符都標志著變量名的終止- 大小寫敏感
- 給變量賦值時,等號周圍不能有任何空白符
- 通常大寫字符為系統默認變量。個人習慣
set
:查看所有變量(含環境變量與自定義變量),以及設置Shell
變量的新變量值
-a
:標示已修改的變量,以供輸出至環境變量-b
:使被中止的后臺程序立刻回報執行狀態-e
:若指令傳回值不等于0
,則立即退出Shell
-f
:取消使用通配符-h
:自動記錄函數的所在位置-H Shell
:可利用!
加<指令編號>
的方式來執行history
中記錄的指令-k
:指令所給的參數都會被視為此指令的環境變量-l
:記錄for
循環的變量名稱-m
:使用監視模式-n
:只讀取指令,而不實際執行-p
:啟動優先順序模式-P
:啟動-P
參數后,執行指令時,會以實際的文件或目錄來取代符號連接-t
:執行完隨后的指令,即退出Shell
-u
:當執行時使用到未定義過的變量,則顯示錯誤信息-v
:顯示Shell
所讀取的輸入值-x
:執行指令后,會先顯示該指令及所下的參數set 11 22 33 44 echo $1 echo "$#" echo "$@" eval echo "\$$#" ------------------------- 11 4 11 22 33 44 44
declare/typeset [-aixrp]
變量
-a
:將變量定義成數組-i
:將變量定義成整數-x
:將變量定義成環境變量-r
:將變量定義成readonly
-p
:顯示變量定義的方式和值+
:取消變量屬性,但是+a
和+r
無效,無法刪除數組和只讀屬性,可以使用unset
刪除數組,但是unset
不能刪除只讀變量declare -i a=3 b=4 c c=a+b echo $c ------------------------- 7
declare -p a ------------------------- typeset -i a=3
local
關鍵字,用來在作用域內創建變量,出了作用域被銷毀
Shell
中定義的變量,默認都是全局變量,即使在函數體中定義,外部也可以訪問。如果想定義僅供函數體中可訪問的本地變量,需要加上local
關鍵字function DoWork { local LG_CAT="LG_Cat" return 0 }
export
為Shell
變量或函數設置導出屬性,成為環境變量。無法對未定義的函數添加導出屬性。同時,重要的一點是,export
的效力僅及于該次登陸操作。注銷或者重新開一個窗口,export
命令給出的環境變量都不存在了
-f
:代表[變量名稱]
為函數名稱-n
:刪除變量的導出屬性。變量實際上并未刪除,只是不會輸出到后續指令的執行環境中-p
:顯示全部擁有導出屬性的變量-pf
:顯示全部擁有導出屬性的函數-nf
:刪除函數的導出屬性--
:在它之后的選項無效
通配符
*
:匹配任意字符串,包括空字符串,不包含對/
字符的匹配。?
:匹配任意單個字符,不能匹配/
字符。[abc]
:匹配a
或者b
或者c
字符。[^abc]
:不匹配a
或者b
或者c
字符。[a-z]
:匹配26
個英文小寫字符中任意一個echo * ------------------------- Cat Cat1 Cat1237
用
set
命令可以查看所有的變量
unset var
命令可以清除變量var
,var
相當于沒有定義過
readonly var
可以把var
變為只讀變量,定義之后不能對var
進行任何更改
函數的聲明
函數的聲明形式:
- 有
funtion
,可以不寫()
。沒有function
,必須寫()
- 函數名和
{
之間必須有空格- 不得聲明形式參數
- 必須在調用函數地方之前,聲明函數
- 無法重載
- 后來的聲明會覆蓋之前的聲明
- 沒有返回值的函數,默認返回函數內最后一條指令的返回值。有返回值的函數,只能返回整數
- 需要獲得函數值,只能通過
$?
獲得。通過=
獲得是空值有
funtion
,可以不寫()
function 函數名 { 函數體 }
沒有
function
,必須寫()
函數名() { 函數體 }
完整寫法
function 函數名() { 函數體 }
案例:
function DoWork { LG_CAT="LG_Cat" echo "logic" return 2 } DoWork echo "$?" echo `DoWork` echo "$?" ------------------------- logic 2 logic 0
可以將
Shell
中函數,看作是定義一個新的命令,因此各個輸入參數直接用空格分隔。命令里面獲得參數方法可以通過:$0...$n
得到。$0
代表函數本身
$#
:傳入的參數的個數$*
:所有的位置參數(作為單個字符串)$@
:所有的位置參數(每個都作為獨立的字符串)$?
:當前Shell
進程中,上一個命令的返回值,如果上一個命令成功執行則$?
的值為0
,否則為其他非零值$$
:當前Shell
進程的pid
$!
:后臺運行的最后一個進程的pid
$-
:顯示Shell
使用的當前選項$_
:之前命令的最后一個參數function DoWork { echo "特殊變量:\n \$#:$#\\n \$0:$0\\n \$1:$1\\n \$2:$2\\n \$*:$*\\n \$@:$@\\n \$$:$$\\n \$-:$-\\n \$_:$_ " return 2 } DoWork "Cat" "LGCat" ------------------------- 特殊變量: $#:2 $0:DoWork $1:Cat $2:LGCat $*:Cat LGCat $@:Cat LGCat $$:70639 $-:569JNRXZghiklm $_:LGCat
參數擴展
通過符號$
獲得參數中存儲的值
間接參數擴展:
${parameter-string}
:當parameter
未設置則替換成string
,不更改parameter
值。否則,不做處理${parameter=string}
:當parameter
未設置則替換成string
,更改parameter
值。否則,不做處理${parameter?string}
:當parameter
沒有設置,則把string
輸出到標準錯誤中。否則,不做處理${parameter+string}
:當parameter
為空的時替換成string
。否則,不做處理${!parameter}
:執行雙重替換bash
,這意味著它接受parameter
的字符串并將其用作變量名。zsh
不支持使用
bash
:declare attr=a a=b echo ${!attr} ------------------------- b
兼容
bash
和zsh
:declare attr=a a=b eval echo "\$$attr" ------------------------- b
冒號后面跟
=
、+
、-
、?
(不能有空格):
${parameter:-string}
:當parameter
未設置或者為空則替換成string
,不更改parameter
值${parameter:=string}
:當parameter
未設置或者為空則替換成string
,更改parameter
值${parameter:?string}
:若變量parameter
不為空,則使用變量parameter
的值。若為空,則把string
輸出到標準錯誤中,并從腳本中退出${parameter:+string}
:當parameter
不為空的時替換成string
。若為空時則不替換或者說是替換空值
子串擴展:
${parameter:offset}
和${parameter:offset:length}
- 從
offset
位置開始截取長度為length
的子串,如果沒有提供length
,則是從offset
開始到結尾offset
可以是負值,且必須與冒號有間隔或者用()
包裹。開始位置是從字符串末尾開始算起,然后取長度為length
的子串。例如:-1
代表是從最后一個字符開始。parameter
是@
,也就是所有的位置參數時,offset
必須從1
開始
替換:
${parameter/pattern/string}
、${parameter//pattern/string}
、${parameter/pattern}
和${parameter//pattern}
- 大小寫敏感
string
為空時,則相當于將匹配的子串刪除。parameter
之后如果是/
,則只匹配遇到的第一個子串parameter
之后如果是//
,則匹配所有的子串
刪除:
${parameter#pattern}
、${parameter##pattern}
、${parameter%pattern}
和${parameter%%pattern}
#
是去掉左邊,%
是去掉右邊- 單一符號是最小匹配,兩個符號是最大匹配
參數長度:
${#parameter}
判斷
多分支語句判斷:
除最后一個分支外(這個分支可以是普通分支,也可以是
*)
分支),其它的每個分支都必須以;;
結尾。;;
代表一個分支的結束,不寫的話會有語法錯誤最后一個分支可以寫
;;
,也可以不寫。因為無論如何,執行到esac
都會結束整個case in
語句語法:
case $變量 in "第一個變量內容") 程序 ;; #結束 *) # 用來托底,沒有匹配到數據 ;; esac
案例:
case $LG_CAT in "cat") echo "名字為cat" ;; "LGCat") echo "名字為LGCat" ;; *) echo "未找到名字" ;; esac
[]
:判斷符號,兩個等號和一個等號,效果類似
- 中括號里面的每個組件都需要空格分隔
- 中括號的變量,使用雙引號
- 中括號的常量,使用單引號或雙引號
一個條件判斷:
if [ condation ]; then 成立 else 不成立 fi
多條件判斷:
if [ condation ]; then 成立 elif [ condation ]; then 成立 else 不成立 fi
案例:
function DoWork { return 2 } DoWork if [[ $? -ne 0 ]]; then echo "函數調用出錯" fi
LG_CAT=Cat if [[ "${LG_CAT}" == "Cat" ]]; then echo "${LG_CAT}" fi
test
命令:
test n1 -eq n2
:
-eq
:相等-ne
:不等-gt
:大于-lt
:小于-ge
:大于等于-le
:小于等于字符串判斷:
-z string
:判斷string
是否為為空,為空則為true
-n string
:判斷string
是否非空,為空則為false
string1 = string2
:字符串是否相等,相等為true
string1 != string2
:字符串是否不等,相等為false
多重條件判斷:
-a
:兩個條件同時成立,為true
-o
:兩個條件任何一個成立,為true
!
:反向文件類型判斷:
-e
:文件名是否存在-f
:該文件名是否存在且是否為文件-d
:該名稱是否存在且為目錄-L
:該名稱是否存在且是否為鏈接文件文件權限檢測:
-r
:是否存在是否有可讀權限-w
:是否存在是否有可寫權限-x
:是否存在是否有可執行權限-s
:是否存在且為非空白文件兩文件比較
-nt
:文件1
是否比文件2
新-ot
:文件1
是否比文件2
舊-ef
:文件1
和文件2
是否為同一個文件if [[ -e "./test與判斷.sh" ]]; then echo "文件存在" else echo "文件不存在" fi
判斷中沒有函數體會報錯,可以使用
:
占位if [ true ]; then : fi
循環
當條件成立,就進行循環:
while [ condation ] #判斷條件 do #循環開始 程序 done #循環結束
案例:
COUNTER=0 while [ $COUNTER -lt 5 ] do echo $COUNTER COUNTER=`expr $COUNTER + 1` done
當條件成立,就終止循環:
until [ condation ] #判斷條件 do #循環開始 程序 done #循環結束
案例:
a=0 until [ ! $a -lt 10 ] do echo $a a=`expr $a + 1` done
按照指定次數循環:
for var in con1 con2 con3 ... do 程序 done
for (( 初始值; 限制值; 執行步長 )) do 程序 done
案例:
for loop in 1 2 3 4 5 do echo "The value is: $loop" done
for (( i = 1; i <= 5; i++ )) do echo "i=$i" done
總結:
如何學習
Shell
- 學語法
- 看腳本
- 抄
常用命令參考
Shell
有兩種方式
- 交互式(
Interactive
)- 批處理(
Batch
)方式
.bashrc
、.bash_profile
和.zshrc
作用與區別
.bashrc
、.bash_profile
是給bash
來使用的.zshrc
是給zsh
來使用的交互式登錄和非登錄
Shell
- 交互式登錄
Shell
:bash
會先查找/etc/profile
文件,如果該文件存在,它將運行文件中列出的命令。然后搜索~/.bash_profile
、~/.bash_login
以及~/.profile
文件,順序讀取- 非登錄
Shell
:讀取~/.bashrc
三種運行方式
sh
:生成一個子Shell
進程source
:不會生成新的進程exec
:用command
進程替換當前Shell
進程執行權限
sh/bash/zsh
:不需要執行權限./script.sh
:需要執行權限source script.sh
:不需要執行權限exec
:需要執行權限