iOS高級強化--012:Shell入門教程

ShellUnix Shell)是一種命令行解釋器,是Unix操作系統下最傳統的人機接口。

Shell腳本是解釋執行的,不需要編譯,和大部分的編程語言很相似,也有基本的變量和流程控制語句。

平時使用Shell有兩種方式:

  • 輸入命令,執行,這種方式稱為交互式(Interactive);
  • 批處理(Batch)方式,用戶事先寫好Shell腳本文件,然后順序執行腳本中的命令。

第一個Shell環境是Thompson Shell,在貝爾實驗室開發并于1971年發布

現代Shell最突出的祖先是被稱為shBourneShell,這是以在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 Falstad1990年開發的。它是一個BourneShell,它使用bashprevious 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_profilebash作為交互式登錄Shell調用時被讀取并執行,而.bashrc對于交互式非登錄Shell被執行

大多數Linux/Unix發行版都使用~/.profile代替~/.bash_profile~/.profile所有Shell都讀取該文件,而~/.bash_profile只有bash才會讀取該文件

~/.zshrczsh的交互式Shell的用戶配置

對于bash,它們的工作方式如下:

讀取適當的內容,執行A,然后執行B,然后執行C,依此類推。B1B2B3表示僅執行找到的那些文件中的第一個。

+----------------+-----------+-----------+------+
|                |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調用,最后在~/.zshrcsource調用~/.bash_profile
編譯器與解釋器

編譯型語言需要編譯處理:

  • 源代碼(source code)-> 預處理器(preprocessor)-> 編譯器(compiler)-> 目標代碼(object code)-> 鏈接器(Linker)-> 可執行程序(executables

解釋型語言需要解釋器處理:

  • 源代碼(source code)-> 解釋器(interpreter
Shell初探

ShebangHashbang):一個由井號和嘆號構成的字符序列#!出現在文本文件的第一行的前兩個字符

#!/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領養,使其父進程ID1

因為所有的進程均有父進程,事實上,所有進程的創建,都可視為子進程創建過程。unix操作系統進程的創建,大致都是進行fork + exec類系統調用

理解子進程的創建執行,需要至少細分到二個步驟,包括:

  • 通過fork創建子進程環境,
  • 通過exec加載并執行進程代碼。

Shell子進程(以下均稱subshell):顧名思義,就是在當前的Shell下,去打開另一個新Shell

PS1提示符定義:提示符設置[\u@\h \w \A #\#]\$

  • \u:用戶賬號名稱
  • \h:主機名縮寫
  • \w:完整工作路徑
  • \A24小時時間
  • \#:第幾個命令
  • \$:提示符,如果是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:打印aliaskeywordfunctionbuilt-infile這五種類型
  • -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 > filecat >> file標準輸入

cat > Cat1237
111
222
  • 使用> file等待輸入,將輸入的內容保存到file中,使用control + d結束輸入。
  • >覆蓋
  • >>累加

使用<<結束輸入,后接一個結束輸入符

cat > Cat1237 << "cat"
111
222
cat
  • cat作為結結束提示符
  • 按照日常規范建議使用eof表示結束提示符

單箭頭和雙箭頭的區別:

對于輸出:

  • 單箭頭:當指定的文件不存在時,創建新文件寫入數據;當文件存在時,清空原文件的內容寫入數據
  • 雙箭頭:當指定的文件不存在時,創建新文件寫入數據;當文件存在時,在原件內容的最后追加寫入數據

對于輸入:

  • 單箭頭:將文件或其他輸入作為標準輸入(<的左邊必須是命令,<右邊的輸入內容作為命令的輸入)
  • 雙箭頭:結束輸入

將標準輸出和錯誤輸出重定向到一個文件上:

stdinstderr無序輸出到one.log

grep "Cat"  file.log > one.log 2>one.log

stdinstderr序有輸出到one.log

grep "Cat"  file.log > one.log 2>&1
  • 首先stdin重定向到one.log,然后使用2>&1表示stderr重定向至stdinstderrstdin之后輸入到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默認的數值運算是整數類型。所以若要進行數學運算,必須使用一些命令,例如:declareexpr、雙括號等

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
}

exportShell變量或函數設置導出屬性,成為環境變量。無法對未定義的函數添加導出屬性。同時,重要的一點是,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命令可以清除變量varvar相當于沒有定義過
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

兼容bashzsh

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

  • 交互式登錄Shellbash會先查找/etc/profile文件,如果該文件存在,它將運行文件中列出的命令。然后搜索~/.bash_profile~/.bash_login以及~/.profile文件,順序讀取
  • 非登錄Shell:讀取~/.bashrc

三種運行方式

  • sh:生成一個子Shell進程
  • source:不會生成新的進程
  • exec:用command進程替換當前Shell進程

執行權限

  • sh/bash/zsh:不需要執行權限
  • ./script.sh:需要執行權限
  • source script.sh:不需要執行權限
  • exec:需要執行權限
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容