shell學習筆記

趙言文發表於2019-02-01

shell的格式

shell可以在直接在命令列下輸入,也可以儲存成shell指令碼檔案執行。當命令簡單並且不需要重複使用,在命令列輸入直接執行即可,否則就寫成指令碼。shell指令碼預設副檔名為.sh。在shell指令碼中,寫入的內容,會預設當成一條命令來執行。

例如:

#!/bin/bash
echo `hello world`
  • 第1行 指定shell指令碼的直譯器
  • 第2行 執行echo命令

將上面的程式碼存為test.sh,並將可執行許可權賦予它chmod +x test.sh ,執行./test.sh執行指令碼。

上面的指令碼將會輸出:

hello world

這和在命令列或者終端模擬器下輸入echo `hello world`並按下回車得到的結果是一樣的

註釋

和所有的程式語言一樣,shell也有註釋,在shell中,#號和它後面的內容來表示一個註釋:

# Print a message
echo "I`m a shell script."

輸出內容

echo用於向輸出流輸出內容,例如:

echo "hello world"

輸入內容

read用於輸入一條內容:

read input
echo $input

上面的程式碼中,read命令從輸入流讀取一個值並賦予input,然後將input的內容列印出來

1. 變數

定義變數和賦值

變數的命名規則和C語言差不多,支援英文字母和下劃線。shell中變數名前不需要宣告型別,變數名後面不能有空格,例如:

var1=`hello`
var2=90

讀取變數

$後接變數名意為讀取一個變數的值,例如:

var="hello"
echo $var

也可以用${var}方式訪問到變數值,例如:

var="hello"
echo ${var}

訪問變數的時候$var${var}是等效的,推薦後者來訪問一個變數

變數作用域

全域性變數

沒有任何命令修飾的變數是一個全域性變數,全域性變數在同一個shell會話中都是有效的。

function func(){
    a=90
}
func
echo $a

輸出:

90

$ a=90
$ echo ${a}
$ bash 
$ echo ${a}

輸出:

90

空值

區域性變數

local命令用於宣告一個區域性變數

function func(){
    local a=90
}
func
echo $a

輸出:

空值

環境變數

用export命令修飾的變數稱為環境變數,在父shell會話中宣告一個環境變數,子shell中都可以訪問

$ export path="/system/bin"
$ bash #建立一個新的shell會話
$ echo ${path}

特殊變數

變數 含義
$0 當前指令碼的檔名
$n(n≥1) 傳遞給指令碼或函式的引數。n 是一個數字,表示第幾個引數。例如,第一個引數是 $1,第二個引數是 $2
$# 傳遞給指令碼或函式的引數個數
$* 傳遞給指令碼或函式的所有引數
$@ 傳遞給指令碼或函式的所有引數
$? 上個命令的退出狀態,或函式的返回值
$$ 當前 Shell 程式 ID。對於 Shell 指令碼,就是這些指令碼所在的程式 ID

$*和$@的區別

  • $*得到所有引數的字串形式
  • $@得到所有引數的陣列形式,可以直接遍歷

2. 獲取一條命令的執行結果

用 ` 將一條命令包裹起來

` 這個符號,在鍵盤上的位置是在Esc鍵的下方

ret=${pwd}
echo ${ret}

在 ` 包裹起來的命令中,也可以訪問到變數

path=`/`
ret=`ls -l ${path}`
echo ${ret}

以$(command)這種方式執行命令

ret=$(pwd)
echo ${ret}

用$(command)這種方式也可以訪問到變數

path=`/`
ret=$(ls -l ${path})
echo ${ret}

上面的例子中,如果想列印命令結果中的換行符,則:

path=`/`
ret=$(ls -l ${path})
echo "${ret}"

$(command)方式來執行命令更加直觀,但是要注意,$(command) 僅在 Bash Shell 中有效,而反引號可在多種 Shell 中都可使用

3. 字串

shell有三種方式可以表示字串

字串的表示

(1)變數名後直接跟上字元

str=hello
echo ${str}

輸出:

hello

這種方式的字串遇到空格就會被終止

(2)單引號

str=hello
echo `${str}`

輸出:

${str}

單引號裡的內容是字串原始的樣子,不存在轉義

(3)雙引號

str=shell
echo "${str}:"hello wolrd""

輸出:

shell:”hello world”

雙引號中可以訪問變數和轉義

獲取字串的長度

str="hello"
echo ${#str}

輸出:

5

字串拼接

兩個變數放在一起訪問就可以拼接

a=`hello`
b=`world`
c=${a}${b}
echo ${c}

輸出:

helloworld

也可以這樣

echo `hello`"world"

字串擷取

(1) 從左邊開始擷取字串,格式:${string: start :length},length可省略,省略時,是擷取到字串末尾

msg="hello world"
echo ${msg: 6: 5}

輸出:

world

(2) 在指定位置擷取字元

  • 擷取chars後面的字元:${string#*chars}

其中,string 表示要擷取的字元,chars 是指定的字元(或者子字串),是萬用字元的一種,表示任意長度的字串。chars連起來使用的意思是:忽略左邊的所有字元,直到遇見 chars(chars 不會被擷取)。

  • 擷取最後一次出現chars的位置後面的內容:${string##*chars}

  • 使用 % 擷取左邊字元
    使用%號可以擷取指定字元(或者子字串)左邊的所有字元,具體格式如下:
    ${string%chars*}

請注意 * 的位置,因為要擷取 chars 左邊的字元,而忽略 chars 右邊的字元,所以 * 應該位於chars的右側。其他方面%和#的用法相同

4. 運算子和流程控制

基本運算

運算子 作用
+ 加(需要結合expr命令使用)
減(需要結合expr命令使用)
* 乘(需要結合expr命令使用)
/ 除(需要結合expr命令使用)
% 求餘(需要結合expr命令使用)
= 賦值
== 判斷數值是否相等,需要結合[]使用
!= 判斷數值是否不相等,需要結合[]使用
a=8
b=4
echo "a=$a,b=$b"
var=`expr ${a} + ${b}`
echo "加法結果:${var}"
var=`expr ${a} - ${b}`
echo "減法結果:${var}"
# 注意:乘號需要轉義
var=`expr ${a} * ${b}`
echo "乘法結果:${var}"
var=`expr ${a} / ${b}`
echo "除法結果:${var}"
var=`expr ${a} % ${b}`
echo "求餘結果:${var}"
var=$[${a} == ${b}]
echo "是否相等:${var}"
var=$[${a} != ${b}]
echo "是否不相等:${var}"

輸出:

a=8,b=4
加法結果:12
減法結果:4
乘法結果:32
除法結果:2
求餘結果:0
是否相等:0
是否不相等:1

上面的例子中,呼叫expr命令和使用[],得到表示式的值,並將它們輸出

請注意表示式兩邊的空格,shell中表示式兩邊要有空格

關係運算

運算子 作用
-eq 全稱:Equal,判斷兩個數是否相等
-ne 全稱:Not equal,判斷兩個數是否不相等
-gt 全稱:Greater than,判斷前面那個數是否大於後面那個數
-lt 全稱:Less than,判斷前面那個數是否小於後面那個數
-ge 全稱:Greater equal,判斷前面那個數是否大於等於後面那個數
-le 全稱:Less than,判斷前面那個數是否小於等於後面那個數

布林運算

運算子 作用
! 非運算
-o 或運算
-a 並運算

邏輯運算

運算子 作用
&& 邏輯並
|| 邏輯或
  • &&連線起來的兩個命令,前面的執行失敗就不執行後面的命令
cd /bin && ls /bin

其實和C語言中的是差不多的,只要前面的條件不滿足,後面那個就不用去執行它了

  • ||連線起來的兩個命令,前面的執行失敗才會執行後面的命令
cd /bin || ls /bin

在這裡順便說一下;這個運算子,這個運算子用於連線多個語句,使多個語句能夠在同一行。用;連線起來的語句,不管前面的執行成不成功,都會執行後面的

mkdir luoye;cd luoye;pwd

檔案判斷

運算子 作用
-e 判斷物件是否存在
-d 判斷物件是否存在,並且為目錄
-f 判斷物件是否存在,並且為常規檔案
-L 判斷物件是否存在,並且為符號連結
-h 判斷物件是否存在,並且為軟連結
-s 判斷物件是否存在,並且長度不為0
-r 判斷物件是否存在,並且可讀
-w 判斷物件是否存在,並且可寫
-x 判斷物件是否存在,並且可執行
-O 判斷物件是否存在,並且屬於當前使用者
-G 判斷物件是否存在,並且屬於當前使用者組
-nt 判斷file1是否比file2新
-ot 判斷file1是否比file2舊

流程控制語句

(1) if語句

if語句格式如下:

if <condition>
then
    #do something
elif <condition>
then
    #do something
else
    #do something
fi

如果想把then和if放同一行

if <condition> ; then
    #do something
elif <condition> ; then
    #do something
else
    #do something
fi

其中elif和else可以省略

例子:

read file

if [ -f ${file} ] ; then
echo `This is normal file.`
elif [ -d ${file} ] ; then
echo `This is dir`
elif [ -c ${file} -o -b ${file} ] ; then
echo `This is device file.`
else
echo `This is unknown file.`
fi

邏輯判斷也可以用test命令,它和[]的作用是一樣的

#!/bin/bash
a=4
b=4
if test $[a+1] -eq $[b+2] 
then
   echo "表示式結果相等"
else
   echo "表示式結果不相等"
fi

輸出:

表示式結果不相等

(2) for 語句

if語句格式如下:

for <var> in [list] 
do
# do something
done

例子:

read input
for val in ${input} ; do
echo "val:${val}"
done

輸入:

1 2 3 4 5

輸出:

val:1

val:2

val:3

val:4

val:5

(3) while 語句

while <condition>
do
    #do something
done

例子:

a=1
sum=0
while [ ${a} -le 100 ] ;do
sum=`expr ${sum} + ${a}`
a=`expr ${a} + 1`
done

echo ${sum}

輸出:

5050

5. 函式

  1. 用function關鍵字來定義一個函式
  2. 直接寫一個函式名來呼叫一個無引數的函式
  3. 函式有引數,呼叫時,在函式名後面寫上引數,多個引數用空格隔開
  4. 呼叫函式時傳遞引數,在函式體內部,通過 $n的形式來獲取引數的值,例如:$1表示第1個引數,$2表示第2個引數…

函式的結構

function foo(){
# do something...
}

函式的用法示例

function foo(){
    local name=$1
    local age=$2
    echo "My name is ${name},I`m ${age} years old."
}
foo "luoye" 26

輸出:

My name is luoye,I`m 26 years old.

6. 重定向

重定向可以理解把一個東西傳送到另個地方

重定向符 作用
output > file 將輸出流重定向到檔案
output >> file 將輸出流追加到檔案末尾
input < file 將檔案的內容重定向到輸入流

輸出到檔案

例子:

echo `hello` > out.txt
echo `world` >> out.txt
cat out.txt

輸出:

hello
world

上面的例子,將hello從輸出流重定向檔案,將world追加到out.txt檔案尾,最後用cat命令讀取並列印out.txt的檔案內容

重定向符還可以配合數字(0,1,2)使用

  • 0 代表標準輸入流
  • 1 代表標準輸出流,上面的例子沒有指定數字,就是預設輸出流
  • 2 代表標準錯誤流
ls / 1> out.txt
cat out.txt

執行上面的命令,會將根目錄下的檔名和目錄名輸出到out.txt

ls /luoye 2> out.txt
cat out.txt

執行上面的命令,會將執行ls /luoye命令時的錯誤資訊輸出到out.txt

ls /;ls /luoye 2>&1

執行上面的程式碼,將錯誤流重定向到輸出流,這種做法在某些場合是很有用的。

特殊檔案

/dev/null

所有重定向到這個檔案的內容都會消失,常常同於忽略錯誤輸出。

ls /luoye 2> /dev/null

如果不存在/luoye這個目錄或者檔案,就沒有什麼提示

/dev/zero

這個檔案會不斷產出空的資料,該檔案常被dd命令使用

dd if=/dev/zero of=out.txt bs=1 count=16

/dev/zero輸入,輸出到out.txt,生成一個大小為16位元組的空檔案

7. 管道

管道是Linux中的一種跨程式通訊的機制,和重定向不同,管道用做程式與程式之間傳送資料。做為Linux中預設的指令碼語言,shell中也是可以使用管道的,在shell中,管道用|表示

(1)使用管道進行資料篩選內容中包含root的行

ls -l /|grep root

這個例子中,ls命令輸出的內容傳給了grep命令進行篩選

(2)也可以同時用多個管道

使用多個管道把資料篩選並統計

ls -l /|grep root|wc -l

這個例子中,ls命令輸出的內容傳給了grep命令進行篩選,然後轉給wc命令統計行數。

為了更好的理解管道,寫兩個指令碼來體驗一下:

in.sh檔案

#! /bin/bash
read msg
echo "Receive :${msg}"

out.sh檔案

#! /bin/bash
echo `hello`

在命令列中執行./out.sh |./in.sh

輸出:

Receive :hello

符合我們預期,字串hello從out.sh傳送到了in.sh

8. 參考

  • http://c.biancheng.net/shell/
  • http://www.runoob.com/linux/linux-shell-process-control.html
  • https://www.cnblogs.com/qlqwjy/p/8684630.html

相關文章