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. 函式
- 用function關鍵字來定義一個函式
- 直接寫一個函式名來呼叫一個無引數的函式
- 函式有引數,呼叫時,在函式名後面寫上引數,多個引數用空格隔開
- 呼叫函式時傳遞引數,在函式體內部,通過 $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