iOS高級(jí)強(qiáng)化--012:Shell入門教程

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

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

平時(shí)使用Shell有兩種方式:

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

第一個(gè)Shell環(huán)境是Thompson Shell,在貝爾實(shí)驗(yàn)室開(kāi)發(fā)并于1971年發(fā)布

現(xiàn)代Shell最突出的祖先是被稱為shBourneShell,這是以在AT&T工作的創(chuàng)始人Stephen Bourne命名的

Shell一直在基于這個(gè)概念,不斷添加各種新功能,演變出很多種的Shell

例如:很早版本的OS X中使用的是:

  • tcsh作為默認(rèn)的Shell。這是由csh(C shell),一種類似C語(yǔ)言的Shell演變而來(lái)

OS X 10.3版與10.4版之后,默認(rèn)的Shell是:

  • bash,由GNU開(kāi)發(fā)

除了默認(rèn)的bash,現(xiàn)在macOS中,默認(rèn)的Shell變成了zsh

這是一種由Paul Falstad1990年開(kāi)發(fā)的。它是一個(gè)BourneShell,它使用bashprevious shell的特性,并添加了更多的特性:

  • 拼寫檢查功能
  • 內(nèi)置的編程特性
  • 友好的交互

與此同時(shí),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作用與區(qū)別

在使用命令行工具時(shí),我們可能會(huì)遇到一些教程,可能需要你把一些配置寫入到.bashrc、.bash_profile或者.zshrc等。那么這幾個(gè)文件到底有什么作用和區(qū)別?

從文件名稱判斷.bashrc、.bash_profile是給bash來(lái)使用的。而.zshrc是給zsh來(lái)使用的

查看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,重新打開(kāi)終端即可

chsh -s /bin/bash

bash切換zsh,重新打開(kāi)終端即可

chsh -s /bin/zsh
交互式登錄和非登錄Shell

當(dāng)調(diào)用Shell時(shí),Shell從一組啟動(dòng)文件中讀取信息并執(zhí)行命令。讀取什么文件就取決于Shell是作為交互式登錄還是非登錄調(diào)用。

換言之,Shell分為交互式的或非交互式的:

  • 交互式Shell是讀取和寫入到用戶終端的Shell程序,用戶在終端上輸入命令,并在回車后立即執(zhí)行
  • 非交互式Shell是與終端不相關(guān)的Shell程序。例如:執(zhí)行腳本時(shí)

交互式Shell可以是登錄Shell,也可以是非登錄Shell

當(dāng)用戶通過(guò)ssh或本地遠(yuǎn)程登錄到終端時(shí),或者使用--login選項(xiàng)啟動(dòng)時(shí),將調(diào)用登錄Shell

zsh --login

當(dāng)bash作為交互式登錄Shell調(diào)用時(shí),bash會(huì)先查找/etc/profile文件,如果該文件存在,它將運(yùn)行文件中列出的命令,然后搜索~/.bash_profile、~/.bash_login以及~/.profile文件,順序讀取

當(dāng)bash作為交互式非登錄Shell調(diào)用時(shí),會(huì)讀取~/.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

所以說(shuō),.bashrc.bash_profile之間的區(qū)別是,.bash_profile當(dāng)bash作為交互式登錄Shell調(diào)用時(shí)被讀取并執(zhí)行,而.bashrc對(duì)于交互式非登錄Shell被執(zhí)行

大多數(shù)Linux/Unix發(fā)行版都使用~/.profile代替~/.bash_profile。~/.profile所有Shell都讀取該文件,而~/.bash_profile只有bash才會(huì)讀取該文件

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

對(duì)于bash,它們的工作方式如下:

讀取適當(dāng)?shù)膬?nèi)容,執(zhí)行A,然后執(zhí)行B,然后執(zhí)行C,依此類推。B1,B2B3表示僅執(zhí)行找到的那些文件中的第一個(gè)。

+----------------+-----------+-----------+------+
|                |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      |           |      |
+----------------+-----------+-----------+------+

對(duì)于zsh,它們的工作方式如下:

讀取適當(dāng)?shù)膬?nèi)容,執(zhí)行A,然后執(zhí)行B,然后執(zhí)行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      |           |      |
+----------------+-----------+-----------+------+

如何確認(rèn)當(dāng)前是登錄還是非登錄Shell?

確認(rèn)當(dāng)前終端tty使用的Shell類型:

echo $0
-------------------------
zsh
  • 前面有-,表示當(dāng)前已登錄
  • 前面沒(méi)有-,表示當(dāng)前非登錄

使用login命令登錄

login
-------------------------
login: Zang
Password:***

再次查看

echo $0
-------------------------
-zsh

配置建議

bash

  • 將配置選項(xiàng)放到~/.bashrc中,然后在~/.bash_profile中通過(guò)source調(diào)用

zsh

  • 建議仍然將配置選項(xiàng)放到~/.bashrc,~/.bash_profile中通過(guò)source調(diào)用,最后在~/.zshrcsource調(diào)用~/.bash_profile
編譯器與解釋器

編譯型語(yǔ)言需要編譯處理:

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

解釋型語(yǔ)言需要解釋器處理:

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

ShebangHashbang):一個(gè)由井號(hào)和嘆號(hào)構(gòu)成的字符序列#!出現(xiàn)在文本文件的第一行的前兩個(gè)字符

#!/bin/bash

操作系統(tǒng)的程序加載器會(huì)分析Shebang后的內(nèi)容,將這些內(nèi)容作為解釋器指令。并調(diào)用該指令,并將載有Shebang的文件路徑作為該解釋器的參數(shù)

env:不同對(duì)操作系統(tǒng),腳本解釋器可能被安裝于系統(tǒng)的不同的目錄,設(shè)置到系統(tǒng)的PATH

env可以在系統(tǒng)的PATH目錄中查找

#!/usr/bin/python
#!/usr/bin/env pyhon
  • 上述命令,使用在用戶路徑中找到的第一個(gè)Python版本

可以通過(guò)指定版本號(hào):

#!/usr/bin/env pythonX.x

env也可以指定搜索目錄:

#!/usr/bin/env -S -P/usr/local/bin:/usr/bin:${PATH} python
  • /usr/local/bin、/usr/bin、系統(tǒng)PATH搜索Python

Shell子進(jìn)程:是從父子進(jìn)程的概念出發(fā)的,unix操作系統(tǒng)的進(jìn)程從init進(jìn)程開(kāi)始(init進(jìn)程為1,而進(jìn)程號(hào)0為系統(tǒng)原始進(jìn)程,以下討論的進(jìn)程原則上不包括進(jìn)程0)均有其對(duì)應(yīng)的子進(jìn)程,就算是由于父進(jìn)程先行結(jié)束導(dǎo)致的孤兒進(jìn)程,也會(huì)被init領(lǐng)養(yǎng),使其父進(jìn)程ID1

因?yàn)樗械倪M(jìn)程均有父進(jìn)程,事實(shí)上,所有進(jìn)程的創(chuàng)建,都可視為子進(jìn)程創(chuàng)建過(guò)程。unix操作系統(tǒng)進(jìn)程的創(chuàng)建,大致都是進(jìn)行fork + exec類系統(tǒng)調(diào)用

理解子進(jìn)程的創(chuàng)建執(zhí)行,需要至少細(xì)分到二個(gè)步驟,包括:

  • 通過(guò)fork創(chuàng)建子進(jìn)程環(huán)境,
  • 通過(guò)exec加載并執(zhí)行進(jìn)程代碼。

Shell子進(jìn)程(以下均稱subshell):顧名思義,就是在當(dāng)前的Shell下,去打開(kāi)另一個(gè)新Shell

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

  • \u:用戶賬號(hào)名稱
  • \h:主機(jī)名縮寫
  • \w:完整工作路徑
  • \A24小時(shí)時(shí)間
  • \#:第幾個(gè)命令
  • \$:提示符,如果是root,提示符為#,否則是$

HOME:代表用戶主文件夾。
SHELL:當(dāng)前使用的是那個(gè)SHELL
PATH:執(zhí)行文件查找路徑

查看PS1提示符:

echo $PS1
-------------------------
%{%f%b%k%}$(build_prompt)

read [-pt]:讀取鍵盤變量

  • -p:提示符
  • -t:等待秒數(shù)

寫入:

read website
Cat

讀取:

echo $website
-------------------------
Cat
注釋

單行注釋

# echo "單行注釋"

多行注釋

  • 方式一:
: << !
   多行注釋方式一:
       echo "多行注釋"
!
  • 方式二:
: << COMMENT
   多行注釋方式二:
       echo "多行注釋"
COMMENT
  • 方式三:
: '
   多行注釋方式三:
       echo "多行注釋"
'
  • 方式四:
if false; then
   多行注釋方式四:
       echo "多行注釋"
fi
  • 方式五
((0)) && {
   多行注釋方式五:
       echo "多行注釋"
}
特殊符號(hào)的使用

雙引號(hào):""

首先,單引號(hào)和雙引號(hào),都是為了解決中間有空格的問(wèn)題。

因?yàn)榭崭裨?code>Shell中作為一個(gè)很典型的分隔符,比如string1=this is astring,這樣執(zhí)行就會(huì)報(bào)錯(cuò)。為了避免這個(gè)問(wèn)題,因此就產(chǎn)生了單引號(hào)和雙引號(hào)。他們的區(qū)別在于,單引號(hào)將剝奪其中的所有字符的特殊含義,而雙引號(hào)中的$(參數(shù)替換)和反引號(hào)(命令替換與$()作用一樣)是例外。所以,兩者基本上沒(méi)有什么區(qū)別,除非在內(nèi)容中遇到了參數(shù)替換符$和命令替換符反引號(hào)

雙引號(hào)常用于包含一組字符串,在雙引號(hào)中,除了$\、`(反引號(hào))有特殊含義外,其余字符(如換行符、回車符等)沒(méi)有特殊含義

echo輸出1&2,由于&Shell中的特殊符號(hào),直接運(yùn)行命令會(huì)報(bào)錯(cuò)的

echo 1&2
-------------------------
[1] 56375
1
cd: no such entry in dir stack
[1]  + 56375 done       echo 1

可以使用雙引號(hào)"",以此解決可能會(huì)產(chǎn)生歧義的問(wèn)題

echo "1&2"
-------------------------
1&2

使用雙引號(hào)""進(jìn)行多行輸入

echo "
1&2
"
-------------------------

1&2

  • 輸出時(shí),前后都有回車。因?yàn)閾Q行符、回車符在雙引號(hào)中沒(méi)有特殊含義,也會(huì)直接輸出

反引號(hào):``

反引號(hào)的功能是命令替換,在反引號(hào)``中的內(nèi)容通常是命令行,程序會(huì)優(yōu)先執(zhí)行反引號(hào)中的內(nèi)容,并使用運(yùn)行結(jié)果替換掉反引號(hào)處的內(nèi)容

echo "I am `echo Cat`"
-------------------------
I am Cat
  • 在按照Shell從上往下,從左往右執(zhí)行規(guī)則的前提下,優(yōu)先執(zhí)行反引號(hào)``的內(nèi)容

單引號(hào):''

單引號(hào)的功能與雙引號(hào)類似,不過(guò)單引號(hào)中的所有字符都沒(méi)有特殊含義

echo '
i am
`echo Cat`
'
-------------------------

i am
`echo Cat`

  • 反引號(hào)``在單引號(hào)''中沒(méi)有生效,因?yàn)閱我?hào)中的所有字符都沒(méi)有特殊含義

$ + 小括號(hào)$()

作用與反引號(hào)``一樣,也是命令替換

echo "i am $(echo Cat)"
-------------------------
i am Cat

$ + 雙小括號(hào)$(())

$(())的功能是進(jìn)行算術(shù)運(yùn)算,括號(hào)中的內(nèi)容為數(shù)學(xué)表達(dá)式

echo "20 + 5 * 6 = $((20 + 5 * 6))"
-------------------------
20 + 5 * 6 = 50
  • $((20 + 5 * 6))返回雙括號(hào)內(nèi)算數(shù)運(yùn)算的結(jié)果

$ + 中括號(hào)$[]

$[]的功能與$(())一樣,都是用于算術(shù)運(yùn)算

echo "20 + 5 * 6 = $[20 + 5 * 6]"
-------------------------
20 + 5 * 6 = 50

$ + 大括號(hào)${}

${}的功能是變量引用,類似于$符,但是${}$的替換范圍更精準(zhǔn)

小括號(hào):()

用來(lái)定義一個(gè)數(shù)組變量

雙小括號(hào):(())

雙小括號(hào)命令允許在比較過(guò)程中使用高級(jí)數(shù)學(xué)表達(dá)式

中括號(hào):[]

單個(gè)的中括號(hào)的功能與test命令一樣,都是用作條件測(cè)試

雙中括號(hào):[[]]

雙中括號(hào)提供了針對(duì)字符串比較的高級(jí)特性,使用雙中括號(hào)[[]]進(jìn)行字符串比較時(shí),可以把右邊的項(xiàng)看做一個(gè)模式,故而可以在[[]]中使用正則表達(dá)式

大括號(hào):{}

大括號(hào)用于括起一個(gè)語(yǔ)句塊

冒號(hào):(:)

作為內(nèi)建命令:占位符、參數(shù)擴(kuò)展和重定向

Shell里面的括號(hào)

  • ${a}:變量a的值, 在不引起歧義的情況下可以省略大括號(hào)
  • $(cmd):命令替換, 和cmd效果相同
  • $((exp)):增強(qiáng)括號(hào)的用法和expr exp效果相同,計(jì)算數(shù)學(xué)表達(dá)式exp的數(shù)值。其中exp只要符合C語(yǔ)言的運(yùn)算規(guī)則即可,甚至三目運(yùn)算符和邏輯表達(dá)式都可以計(jì)算。可以省略$

例如:

for((i=0;i<5;i++))
# 如果不使用雙括號(hào)
for i in seq 0 4
for i in {0..4}
if (($i<5))
# 如果不使用雙括號(hào)
if [ $i -lt 5 ]

expr命令:

是一款表達(dá)式計(jì)算工具,使用它完成表達(dá)式的求值操作

expr 1 + 2 + 3 - 4
-------------------------
2

語(yǔ)法要求,中間的空格必須加

eval命令:

對(duì)后面的cmdLine進(jìn)行兩遍掃描,如果第一遍掃描后,cmdLine是個(gè)普通命令,則執(zhí)行此命令;如果cmdLine中含有變量的間接引用,則保證間接引用的語(yǔ)義

type命令:

顯示命令屬性,會(huì)顯示該命令所在的位置

type [-aftpP] name [name,...]

  • -a:打印name的所有可能情況
  • -f:不會(huì)去查找function
  • -t:打印alias、keyword、functionbuilt-in、file這五種類型
  • -p:如果type -t name輸出file,那么會(huì)打印name所在路徑
  • -P:不管type -t name是不是輸出file,都會(huì)去搜索name所在路徑。例如:type -P ls,盡管type -t ls打印的是alias(因?yàn)?code>alias的優(yōu)先級(jí)高于file),但是仍然會(huì)搜索出ls所在的路徑/bin/ls
type -a ls
-------------------------
ls is an alias for ls -G
ls is /bin/ls

如果type不加任何選項(xiàng),直接加一個(gè)或者多個(gè)name,那么會(huì)依次打印這些name的類型。只有所有name的類型都能成功打印,type才返回成功。否則,只要任何一個(gè)name類型無(wú)法打印,那么就返回失敗

echo命令:

按照規(guī)則打印字符串

echo [-ne][字符串]

  • -n:不要在最后自動(dòng)換行
  • -e:若字符串中出現(xiàn)以下字符,則特別加以處理:
  • \a:發(fā)出警告;
  • \b:刪除前一個(gè)字符;
  • \c:不產(chǎn)生進(jìn)一步輸出(\c后面的字符不會(huì)輸出);
  • \f:換行但光標(biāo)仍舊停留在原來(lái)的位置;
  • \n:換行且光標(biāo)移至行首;
  • \r:光標(biāo)移至行首,但不換行;
  • \t:插入tab;
  • \v:與\f相同;
  • \\:插入\字符;
  • \nnn:插入nnn(八進(jìn)制)所代表的ASCII字符;

echo命令打印帶有色彩的文字:

  • 文字色:echo -e "\e[1;31mThis is red text\e[0m"
    \e[1;31m:將顏色設(shè)置為紅色
    \e[0m:將顏色重新置回
    顏色碼:重置=0,黑色=30,紅色=31,綠色=32,黃色=33,藍(lán)色=34,洋紅=35,青色=36,白色=37
  • 背景色:echo -e "\e[1;42mGreed Background\e[0m"
    顏色碼:重置=0,黑色=40,紅色=41,綠色=42,黃色=43,藍(lán)色=44,洋紅=45,青色=46,白色=47
  • 文字閃動(dòng):echo -e "\033[37;31;5mLogic Cat 帥帥帥...\033[39;49;0m"
    紅色數(shù)字處還有其他數(shù)字參數(shù):0關(guān)閉所有屬性、1設(shè)置高亮度(加粗)、4下劃線、5閃爍、7反顯、8消隱

grep命令:

用于查找文件里符合條件的字符串

創(chuàng)建Common Symbol目錄,Common Symbol目錄下創(chuàng)建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:其中--表示當(dāng)前命令已經(jīng)沒(méi)有參數(shù)要添加了,后面的內(nèi)容都是傳遞給命令的
  • 可以通過(guò)--的方式,將內(nèi)容和參數(shù)區(qū)分開(kāi)

路徑中包含空格,則有三種不同的解決策略:

  • \轉(zhuǎn)義
grep weak --color=auto -- ../Common\ Symbol/test.m
  • 加雙引號(hào)
grep weak --color=auto -- "../Common Symbol/test.m"
  • 加單引號(hào)
grep weak --color=auto -- '../Common Symbol/test.m'

錯(cuò)誤寫法:

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
  • 使用雙引號(hào)或單引號(hào),里面不能再加\轉(zhuǎn)義
標(biāo)準(zhǔn)輸出 & 輸入 & 錯(cuò)誤輸出

標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出可以將內(nèi)容重定向輸出到指定的設(shè)備(如打印機(jī))或文件中,標(biāo)準(zhǔn)輸入可以使用文件或其他輸入替換手動(dòng)輸入。

標(biāo)準(zhǔn)輸出(stdout):代碼為1,使用>>>

使用>1>以覆蓋的方式,將正確的數(shù)據(jù)輸出到指定到文件或設(shè)備

echo "LGCat" > Cat
echo "LGCat123" > Cat
  • 目錄下生成Cat文件,只會(huì)記錄最后輸出的LGCat123

使用>>1>>以累加到方式,將正確到數(shù)據(jù)輸出到指定到文件或者設(shè)備上

echo "LGCat" >> Cat1
echo "LGCat123" >> Cat1
  • 目錄下生成Cat1文件,將兩次輸出內(nèi)容累加記錄

標(biāo)準(zhǔn)錯(cuò)誤輸出(stderr):代碼為2,使用2>2>>

使用2>以覆蓋的方式,將錯(cuò)誤的數(shù)據(jù)輸出到指定到文件或設(shè)備

echo "LGCat" 2> Cat

使用2>>以累加的方式,將錯(cuò)誤的數(shù)據(jù)輸出到指定到文件或設(shè)備

echo "LGCat" 2>> Cat1

標(biāo)準(zhǔn)輸入(stdin):代碼為0,使用<<<

使用<標(biāo)準(zhǔn)輸入

cat < Cat
-------------------------
LGCat123

使用cat > filecat >> file標(biāo)準(zhǔn)輸入

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

使用<<結(jié)束輸入,后接一個(gè)結(jié)束輸入符

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

單箭頭和雙箭頭的區(qū)別:

對(duì)于輸出:

  • 單箭頭:當(dāng)指定的文件不存在時(shí),創(chuàng)建新文件寫入數(shù)據(jù);當(dāng)文件存在時(shí),清空原文件的內(nèi)容寫入數(shù)據(jù)
  • 雙箭頭:當(dāng)指定的文件不存在時(shí),創(chuàng)建新文件寫入數(shù)據(jù);當(dāng)文件存在時(shí),在原件內(nèi)容的最后追加寫入數(shù)據(jù)

對(duì)于輸入:

  • 單箭頭:將文件或其他輸入作為標(biāo)準(zhǔn)輸入(<的左邊必須是命令,<右邊的輸入內(nèi)容作為命令的輸入)
  • 雙箭頭:結(jié)束輸入

將標(biāo)準(zhǔn)輸出和錯(cuò)誤輸出重定向到一個(gè)文件上:

stdinstderr無(wú)序輸出到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重定向至stdin,stderrstdin之后輸入到one.log

簡(jiǎn)寫:

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

使用>/dev/null將錯(cuò)誤的數(shù)據(jù)丟棄,只顯示正確的數(shù)據(jù)

echo "123" > /dev/null
  • /dev/null代表linux的空設(shè)備文件,所有往這個(gè)文件里面寫入的內(nèi)容都會(huì)丟失,俗稱“黑洞”

使用&>或者>&將正確的數(shù)據(jù)和錯(cuò)誤的數(shù)據(jù)寫入同一個(gè)文件

echo "111" &> Cat
echo "222" >& Cat

使用1>&2正確返回值傳遞給2輸出通道,&2表示2輸出通道

echo "111222" 1>&2

使用2>&1錯(cuò)誤返回值傳遞給1輸出通道,&1表示1輸出通道

echo "111222" 2>&1

cmd;cmd不考慮命令相關(guān)性,連續(xù)執(zhí)行。

echo "Cat1";echo "Cat2"
-------------------------
Cat1
Cat2

當(dāng)前一個(gè)命令執(zhí)行成功會(huì)回傳一個(gè)$?=0的值

e
-------------------------
zsh: command not found: e
-------------------------
echo $?
-------------------------
127

cmd1 && cmd2如果第一個(gè)命令的$?0,則執(zhí)行第二個(gè)命令

echo "Cat1" && echo "Cat2"
-------------------------
Cat1
Cat2

cmd1 || cmd2如果第一個(gè)命令的$?0,則不執(zhí)行第二個(gè)命令。否則執(zhí)行第二個(gè)命令

e || echo "Cat"
-------------------------
zsh: command not found: e
Cat

|:管道僅能處理前面一個(gè)命令傳來(lái)的正確信息,將正確信息作為stdin傳給下一個(gè)命令

echo "test.m" | grep weak
  • 管道命令只處理前一個(gè)命令正確輸出,不處理錯(cuò)誤輸出
  • 管道命令右邊命令,必須能夠接收標(biāo)準(zhǔn)輸入流命令才行
  • 大多數(shù)命令都不接受標(biāo)準(zhǔn)輸入作為參數(shù),只能直接在命令行輸入?yún)?shù),這導(dǎo)致無(wú)法用管道命令傳遞參數(shù)

xargs:是將標(biāo)準(zhǔn)輸入轉(zhuǎn)為命令行參數(shù)

echo "Cat1237" | xargs cat
-------------------------
111
222

-:減號(hào)在一些命令中表示從標(biāo)準(zhǔn)輸入(stdin)中獲取內(nèi)容

echo "Cat1237" | xargs cat -
-------------------------
111
222
三種運(yùn)行方式

sh

  • 使用$sh script.sh執(zhí)行腳本時(shí),當(dāng)前Shell是父進(jìn)程,生成一個(gè)子Shell進(jìn)程,在子Shell中執(zhí)行腳本
  • 腳本執(zhí)行完畢,退出子Shell,回到當(dāng)前Shell$./script.sh$sh script.sh等效。也叫fork方式

source

  • 使用$source script.sh方式,在當(dāng)前上下文中執(zhí)行腳本,不會(huì)生成新的進(jìn)程。腳本執(zhí)行完畢,回到當(dāng)前Shell
  • $sh script.sh$source script.sh等效。不需要有"執(zhí)行權(quán)限"

exec

  • 使用exec ./scriptsh方式,會(huì)用command進(jìn)程替換當(dāng)前Shell進(jìn)程,并且保持pid不變
  • 執(zhí)行完畢,直接退出,不回到之前的Shell環(huán)境

執(zhí)行權(quán)限

  • sh/bash/zsh:不需要執(zhí)行權(quán)限
  • ./script.sh:需要執(zhí)行權(quán)限
  • source script.sh:不需要執(zhí)行權(quán)限
  • exec:需要執(zhí)行權(quán)限
變量的定義

Shell變量默認(rèn)為字符串。Shell不關(guān)心這個(gè)串是什么含義

declare a=3 b=4 c
c=a+b
echo $c
-------------------------
a+b

Shell默認(rèn)的數(shù)值運(yùn)算是整數(shù)類型。所以若要進(jìn)行數(shù)學(xué)運(yùn)算,必須使用一些命令,例如:declare、expr、雙括號(hào)等

declare a=`expr 3 + 4` b=$((1+1))
echo $a;echo $b
-------------------------
7
2

Shell變量可分為兩類:

  • 局部變量:只在創(chuàng)建它們的Shell中可用。在函數(shù)內(nèi)定義,函數(shù)執(zhí)行后就被刪除
  • 環(huán)境變量:可以在創(chuàng)建它們的Shell及其派生出來(lái)的任意子進(jìn)程中使用。在整個(gè)腳本執(zhí)行期間,只要沒(méi)有被刪除就一直存在
export a=3
echo $a
-------------------------
3

定義規(guī)則:

  • 變量名必須以字母或下劃線字符開(kāi)頭,其余的字符可以是字母、數(shù)字(0~9)或下劃線字符。任何其他的字符都標(biāo)志著變量名的終止
  • 大小寫敏感
  • 給變量賦值時(shí),等號(hào)周圍不能有任何空白符
  • 通常大寫字符為系統(tǒng)默認(rèn)變量。個(gè)人習(xí)慣

set:查看所有變量(含環(huán)境變量與自定義變量),以及設(shè)置Shell變量的新變量值

  • -a:標(biāo)示已修改的變量,以供輸出至環(huán)境變量
  • -b:使被中止的后臺(tái)程序立刻回報(bào)執(zhí)行狀態(tài)
  • -e:若指令傳回值不等于0,則立即退出Shell
  • -f:取消使用通配符
  • -h:自動(dòng)記錄函數(shù)的所在位置
  • -H Shell:可利用!<指令編號(hào)>的方式來(lái)執(zhí)行history中記錄的指令
  • -k:指令所給的參數(shù)都會(huì)被視為此指令的環(huán)境變量
  • -l:記錄for循環(huán)的變量名稱
  • -m:使用監(jiān)視模式
  • -n:只讀取指令,而不實(shí)際執(zhí)行
  • -p:?jiǎn)?dòng)優(yōu)先順序模式
  • -P:?jiǎn)?dòng)-P參數(shù)后,執(zhí)行指令時(shí),會(huì)以實(shí)際的文件或目錄來(lái)取代符號(hào)連接
  • -t:執(zhí)行完隨后的指令,即退出Shell
  • -u:當(dāng)執(zhí)行時(shí)使用到未定義過(guò)的變量,則顯示錯(cuò)誤信息
  • -v:顯示Shell所讀取的輸入值
  • -x:執(zhí)行指令后,會(huì)先顯示該指令及所下的參數(shù)
set 11 22 33 44
echo $1
echo "$#"
echo "$@"
eval echo "\$$#"
-------------------------
11
4
11 22 33 44
44

declare/typeset [-aixrp]變量

  • -a:將變量定義成數(shù)組
  • -i:將變量定義成整數(shù)
  • -x:將變量定義成環(huán)境變量
  • -r:將變量定義成readonly
  • -p:顯示變量定義的方式和值
  • +:取消變量屬性,但是+a+r無(wú)效,無(wú)法刪除數(shù)組和只讀屬性,可以使用unset刪除數(shù)組,但是unset不能刪除只讀變量
declare -i a=3 b=4 c
c=a+b
echo $c
-------------------------
7
declare -p a
-------------------------
typeset -i a=3

local關(guān)鍵字,用來(lái)在作用域內(nèi)創(chuàng)建變量,出了作用域被銷毀

Shell中定義的變量,默認(rèn)都是全局變量,即使在函數(shù)體中定義,外部也可以訪問(wèn)。如果想定義僅供函數(shù)體中可訪問(wèn)的本地變量,需要加上local關(guān)鍵字

function DoWork {
   local LG_CAT="LG_Cat"
   return 0
}

exportShell變量或函數(shù)設(shè)置導(dǎo)出屬性,成為環(huán)境變量。無(wú)法對(duì)未定義的函數(shù)添加導(dǎo)出屬性。同時(shí),重要的一點(diǎn)是,export的效力僅及于該次登陸操作。注銷或者重新開(kāi)一個(gè)窗口,export命令給出的環(huán)境變量都不存在了

  • -f:代表[變量名稱]為函數(shù)名稱
  • -n:刪除變量的導(dǎo)出屬性。變量實(shí)際上并未刪除,只是不會(huì)輸出到后續(xù)指令的執(zhí)行環(huán)境中
  • -p:顯示全部擁有導(dǎo)出屬性的變量
  • -pf:顯示全部擁有導(dǎo)出屬性的函數(shù)
  • -nf:刪除函數(shù)的導(dǎo)出屬性
  • --:在它之后的選項(xiàng)無(wú)效

通配符

  • *:匹配任意字符串,包括空字符串,不包含對(duì)/字符的匹配。
  • ?:匹配任意單個(gè)字符,不能匹配/字符。
  • [abc]:匹配a或者b或者c字符。
  • [^abc]:不匹配a或者b或者c字符。
  • [a-z]:匹配26個(gè)英文小寫字符中任意一個(gè)
echo *
-------------------------
Cat Cat1 Cat1237

set命令可以查看所有的變量
unset var命令可以清除變量varvar相當(dāng)于沒(méi)有定義過(guò)
readonly var可以把var變?yōu)橹蛔x變量,定義之后不能對(duì)var進(jìn)行任何更改

函數(shù)的聲明

函數(shù)的聲明形式:

  • funtion,可以不寫()。沒(méi)有function,必須寫()
  • 函數(shù)名和{之間必須有空格
  • 不得聲明形式參數(shù)
  • 必須在調(diào)用函數(shù)地方之前,聲明函數(shù)
  • 無(wú)法重載
  • 后來(lái)的聲明會(huì)覆蓋之前的聲明
  • 沒(méi)有返回值的函數(shù),默認(rèn)返回函數(shù)內(nèi)最后一條指令的返回值。有返回值的函數(shù),只能返回整數(shù)
  • 需要獲得函數(shù)值,只能通過(guò)$?獲得。通過(guò)=獲得是空值

funtion,可以不寫()

function 函數(shù)名 {
   函數(shù)體
}

沒(méi)有function,必須寫()

函數(shù)名() {
   函數(shù)體
}

完整寫法

function 函數(shù)名() {
   函數(shù)體
}

案例:

function DoWork {
   LG_CAT="LG_Cat"
   echo "logic"
   return 2
}
DoWork
echo "$?"
echo `DoWork`
echo "$?"
-------------------------
logic
2
logic
0

可以將Shell中函數(shù),看作是定義一個(gè)新的命令,因此各個(gè)輸入?yún)?shù)直接用空格分隔。命令里面獲得參數(shù)方法可以通過(guò):$0...$n得到。$0代表函數(shù)本身

  • $#:傳入的參數(shù)的個(gè)數(shù)
  • $*:所有的位置參數(shù)(作為單個(gè)字符串)
  • $@:所有的位置參數(shù)(每個(gè)都作為獨(dú)立的字符串)
  • $?:當(dāng)前Shell進(jìn)程中,上一個(gè)命令的返回值,如果上一個(gè)命令成功執(zhí)行則$?的值為0,否則為其他非零值
  • $$:當(dāng)前Shell進(jìn)程的pid
  • $!:后臺(tái)運(yùn)行的最后一個(gè)進(jìn)程的pid
  • $-:顯示Shell使用的當(dāng)前選項(xiàng)
  • $_:之前命令的最后一個(gè)參數(shù)
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
參數(shù)擴(kuò)展

通過(guò)符號(hào)$獲得參數(shù)中存儲(chǔ)的值

間接參數(shù)擴(kuò)展:

  • ${parameter-string}:當(dāng)parameter未設(shè)置則替換成string,不更改parameter值。否則,不做處理
  • ${parameter=string}:當(dāng)parameter未設(shè)置則替換成string,更改parameter值。否則,不做處理
  • ${parameter?string}:當(dāng)parameter沒(méi)有設(shè)置,則把string輸出到標(biāo)準(zhǔn)錯(cuò)誤中。否則,不做處理
  • ${parameter+string}:當(dāng)parameter為空的時(shí)替換成string。否則,不做處理
  • ${!parameter}:執(zhí)行雙重替換bash,這意味著它接受parameter的字符串并將其用作變量名。zsh不支持

使用bash

declare attr=a a=b
echo ${!attr}
-------------------------
b

兼容bashzsh

declare attr=a a=b
eval echo "\$$attr"
-------------------------
b

冒號(hào)后面跟=+、-、?(不能有空格):

  • ${parameter:-string}:當(dāng)parameter未設(shè)置或者為空則替換成string,不更改parameter
  • ${parameter:=string}:當(dāng)parameter未設(shè)置或者為空則替換成string,更改parameter
  • ${parameter:?string}:若變量parameter不為空,則使用變量parameter的值。若為空,則把string輸出到標(biāo)準(zhǔn)錯(cuò)誤中,并從腳本中退出
  • ${parameter:+string}:當(dāng)parameter不為空的時(shí)替換成string。若為空時(shí)則不替換或者說(shuō)是替換空值

子串?dāng)U展:${parameter:offset}${parameter:offset:length}

  • offset位置開(kāi)始截取長(zhǎng)度為length的子串,如果沒(méi)有提供length,則是從offset開(kāi)始到結(jié)尾
  • offset可以是負(fù)值,且必須與冒號(hào)有間隔或者用()包裹。開(kāi)始位置是從字符串末尾開(kāi)始算起,然后取長(zhǎng)度為length的子串。例如:-1代表是從最后一個(gè)字符開(kāi)始。
  • parameter@,也就是所有的位置參數(shù)時(shí),offset必須從1開(kāi)始

替換:${parameter/pattern/string}、${parameter//pattern/string}${parameter/pattern}${parameter//pattern}

  • 大小寫敏感
  • string為空時(shí),則相當(dāng)于將匹配的子串刪除。 parameter之后如果是/,則只匹配遇到的第一個(gè)子串
  • parameter之后如果是//,則匹配所有的子串

刪除:${parameter#pattern}、${parameter##pattern}${parameter%pattern}${parameter%%pattern}

  • #是去掉左邊,%是去掉右邊
  • 單一符號(hào)是最小匹配,兩個(gè)符號(hào)是最大匹配

參數(shù)長(zhǎng)度:${#parameter}

判斷

多分支語(yǔ)句判斷:

除最后一個(gè)分支外(這個(gè)分支可以是普通分支,也可以是*)分支),其它的每個(gè)分支都必須以;;結(jié)尾。;;代表一個(gè)分支的結(jié)束,不寫的話會(huì)有語(yǔ)法錯(cuò)誤

最后一個(gè)分支可以寫;;,也可以不寫。因?yàn)闊o(wú)論如何,執(zhí)行到esac都會(huì)結(jié)束整個(gè)case in語(yǔ)句

語(yǔ)法:

case $變量 in
   "第一個(gè)變量?jī)?nèi)容")
       程序
       ;;    #結(jié)束
   *)    # 用來(lái)托底,沒(méi)有匹配到數(shù)據(jù)
       ;;
esac

案例:

case $LG_CAT in
   "cat")
       echo "名字為cat"
       ;;
   "LGCat")
       echo "名字為L(zhǎng)GCat"
       ;;
   *)
       echo "未找到名字"
       ;;
esac

[]:判斷符號(hào),兩個(gè)等號(hào)和一個(gè)等號(hào),效果類似

  • 中括號(hào)里面的每個(gè)組件都需要空格分隔
  • 中括號(hào)的變量,使用雙引號(hào)
  • 中括號(hào)的常量,使用單引號(hào)或雙引號(hào)

一個(gè)條件判斷:

if [ condation ]; then
   成立
else
   不成立
fi

多條件判斷:

if [ condation ]; then
   成立
elif [ condation ]; then
   成立
else
   不成立
fi

案例:

function DoWork {
   return 2
}

DoWork

if [[ $? -ne 0 ]]; then
   echo "函數(shù)調(diào)用出錯(cuò)"
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:兩個(gè)條件同時(shí)成立,為true
  • -o:兩個(gè)條件任何一個(gè)成立,為true
  • !:反向

文件類型判斷:

  • -e:文件名是否存在
  • -f:該文件名是否存在且是否為文件
  • -d:該名稱是否存在且為目錄
  • -L:該名稱是否存在且是否為鏈接文件

文件權(quán)限檢測(cè):

  • -r:是否存在是否有可讀權(quán)限
  • -w:是否存在是否有可寫權(quán)限
  • -x:是否存在是否有可執(zhí)行權(quán)限
  • -s:是否存在且為非空白文件

兩文件比較

  • -nt文件1是否比文件2
  • -ot文件1是否比文件2
  • -ef文件1文件2是否為同一個(gè)文件
if [[ -e "./test與判斷.sh" ]]; then
   echo "文件存在"
else
   echo "文件不存在"
fi

判斷中沒(méi)有函數(shù)體會(huì)報(bào)錯(cuò),可以使用:占位

if [ true ]; then
   :
fi
循環(huán)

當(dāng)條件成立,就進(jìn)行循環(huán):

while [ condation ]   #判斷條件
do   #循環(huán)開(kāi)始
  程序
done   #循環(huán)結(jié)束

案例:

COUNTER=0
while [ $COUNTER -lt 5 ]
do
  echo $COUNTER
  COUNTER=`expr $COUNTER + 1`
done

當(dāng)條件成立,就終止循環(huán):

until [ condation ]   #判斷條件
do   #循環(huán)開(kāi)始
  程序
done   #循環(huán)結(jié)束

案例:

a=0
until [ ! $a -lt 10 ]
do
  echo $a
  a=`expr $a + 1`
done

按照指定次數(shù)循環(huán):

for var in con1 con2 con3 ...
do
  程序
done
for (( 初始值; 限制值; 執(zhí)行步長(zhǎng) ))
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
總結(jié):

如何學(xué)習(xí)Shell

  • 學(xué)語(yǔ)法
  • 看腳本

常用命令參考

Shell有兩種方式

  • 交互式(Interactive
  • 批處理(Batch)方式

.bashrc、.bash_profile.zshrc作用與區(qū)別

  • .bashrc.bash_profile是給bash來(lái)使用的
  • .zshrc是給zsh來(lái)使用的

交互式登錄和非登錄Shell

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

三種運(yùn)行方式

  • sh:生成一個(gè)子Shell進(jìn)程
  • source:不會(huì)生成新的進(jìn)程
  • exec:用command進(jìn)程替換當(dāng)前Shell進(jìn)程

執(zhí)行權(quán)限

  • sh/bash/zsh:不需要執(zhí)行權(quán)限
  • ./script.sh:需要執(zhí)行權(quán)限
  • source script.sh:不需要執(zhí)行權(quán)限
  • exec:需要執(zhí)行權(quán)限
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容