目錄
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的內容打印出來。詳情查看:shell read命令
1. 變量
1.1 定義變量和賦值
變量的命名規則和C語言差不多,支持英文字母和下劃線。shell中變量名前不需要聲明類型,變量名後面不能有空格,例如:
var1='hello'
var2=90
1.2 讀取變量
var="hello"
#二者等價,推薦後者
echo $var
#或者
echo ${var}
1.3 變量作用域
1.3.1 全局變量
沒有任何命令修飾的變量是一個全局變量,全局變量在同一個shell會話中都是有效的。
function func(){
a=90
}
func
echo $a #輸出90
1.3.2 局部變量
local命令用於聲明一個局部變量
function func(){
local a=90
}
func
echo $a #輸出空值
1.3.3 環境變量
用export命令修飾的變量稱爲環境變量,在父shell會話中聲明一個環境變量,子shell中都可以訪問
$ export path="/system/bin"
$ bash #創建一個新的shell會話
$ echo ${path}
1.3.4 特殊變量
變量 | 含義 |
---|---|
$0 | 當前腳本的文件名 |
$n(n≥1) | 傳遞給腳本或函數的參數。n 是一個數字,表示第幾個參數。例如,第一個參數是1,第二個參數是2 |
$# | 傳遞給腳本或函數的參數個數 |
$* | 傳遞給腳本或函數的所有參數 |
$@ | 傳遞給腳本或函數的所有參數 |
$? | 上個命令的退出狀態,或函數的返回值 |
$$ | 當前 Shell 進程 ID。對於 Shell 腳本,就是這些腳本所在的進程 ID |
1.3.5 *和@的區別
- $*得到所有參數被當成字符串
- $@得到所有參數都會被當成獨立的參數
#!/bin/bash
for val in "$*"
do
echo "\$@ : ${val}"
done
for val in "$@"
do
echo "\$* : ${val}"
done
假如將上面的代碼存爲test.sh
,運行./test.sh 1 2 3
將輸出:
$@ : 1 2 3
$* : 1
$* : 2
$* : 3
2. 獲取一條命令的執行結果
2.1 用 ` 將一條命令包裹起來
` 這個符號,在鍵盤上的位置是在Esc鍵的下方
ret=`pwd`
echo ${ret}
在 ` 包裹起來的命令中,也可以訪問到變量
path='/'
ret=`ls -l ${path}`
echo ${ret}
2.2 以$(command)這種方式執行命令
ret=$(pwd)
echo ${ret}
用$(command)這種方式也可以訪問到變量
path='/'
ret=$(ls -l ${path})
echo ${ret}
上面的例子中,如果想打印命令結果中的換行符,則:
path='/'
ret=$(ls -l ${path})
echo "${ret}"
(command) 僅在 Bash Shell 中有效,而反引號可在多種 Shell 中都可使用
3. 字符串
shell有三種方式可以表示字符串
3.1 字符串的表示
(1)變量名後直接跟上字符。這種方式的字符串遇到空格就會被終止
str=hello
echo ${str} #輸出:hello
(2)單引號。單引號裏的內容是字符串原始的樣子,不存在轉義
str=hello
echo '${str}' #輸出: ${str}
(3)雙引號。雙引號中可以訪問變量和轉義
str=shell
echo "${str}:\"hello wolrd\"" #輸出: shell:"hello world"
3.2 獲取字符串的長度
str="hello"
echo ${#str} #輸出: 5
3.3 字符串拼接
兩個變量放在一起訪問就可以拼接
a='hello'
b='world'
c=${a}${b}
echo ${c} #輸出: helloworld
echo "${a} ${b}" #輸出: hello world
3.4 字符串截取
(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. 運算符和流程控制
4.1 基本運算
運算符 | 作用 |
---|---|
+ | 加(需要結合expr命令使用) |
- | 減(需要結合expr命令使用) |
* | 乘(需要結合expr命令使用) |
/ | 除(需要結合expr命令使用) |
% | 求餘(需要結合expr命令使用) |
= | 賦值 |
== | 判斷數值是否相等,需要結合[] 使用 |
!= | 判斷數值是否不相等,需要結合[] 使用 |
a=8
b=4
echo "a=$a,b=$b"
var=`expr ${a} + ${b}`
echo "加法結果:${var}" #輸出: 12
var=`expr ${a} - ${b}`
echo "減法結果:${var}" #輸出: 4
# 注意:乘號需要轉義
var=`expr ${a} \* ${b}`
echo "乘法結果:${var}" #輸出: 32
var=`expr ${a} / ${b}`
echo "除法結果:${var}" #輸出: 2
var=`expr ${a} % ${b}`
echo "求餘結果:${var}" #輸出: 0
var=$[${a} == ${b}]
echo "是否相等:${var}" #輸出: 0
var=$[${a} != ${b}]
echo "是否不相等:${var}" #輸出: 1
上面的例子中,調用expr命令和使用[]
,得到表達式的值,並將它們輸出
請注意表達式兩邊的空格,shell中表達式兩邊要有空格
4.2 關係運算
運算符 | 作用 |
---|---|
-eq | 全稱:Equal,判斷兩個數是否相等 |
-ne | 全稱:Not equal,判斷兩個數是否不相等 |
-gt | 全稱:Greater than,判斷前面那個數是否大於後面那個數 |
-lt | 全稱:Less than,判斷前面那個數是否小於後面那個數 |
-ge | 全稱:Greater equal,判斷前面那個數是否大於等於後面那個數 |
-le | 全稱:Less than,判斷前面那個數是否小於等於後面那個數 |
4.3 布爾運算
運算符 | 作用 |
---|---|
! | 非運算 |
-o | 或運算 |
-a | 並運算 |
a=10
b=20
if [ $a != $b ]
then
echo "$a != $b : a 不等於 b"
else
echo "$a == $b: a 等於 b"
fi
#輸出:10 != 20 : a 不等於 b
if [ $a -lt 100 -o $b -gt 100 ]
then
echo "$a 小於 100 或 $b 大於 100 : 返回 true"
else
echo "$a 小於 100 或 $b 大於 100 : 返回 false"
fi
#輸出:10 小於 100 或 20 大於 100 : 返回 true
4.4 邏輯運算
運算符 | 作用 |
---|---|
|| | 邏輯或 |
&& | 邏輯並 |
使用雙中括號判斷,其他運算符都用單中括號。如:
a=10
b=20
if [[ $a -lt 100 && $b -gt 100 ]]
then
echo "返回 true"
else
echo "返回 false"
fi
#輸出: 返回 false
4.5字符串判斷
運算符 | 說明 | 舉例 |
---|---|---|
= | 檢測兩個字符串是否相等,相等返回 true。 | [ $a = $b ] 返回 false。 |
!= | 檢測兩個字符串是否相等,不相等返回 true。 | [ $a != $b ] 返回 true。 |
-z | 檢測字符串長度是否爲0,爲0返回 true。 | [ -z $a ] 返回 false。 |
-n | 檢測字符串長度是否爲0,不爲0返回 true。 | [ -n "$a" ] 返回 true。 |
$ | 檢測字符串是否爲空,不爲空返回 true。 | [ $a ] 返回 true。 |
4.6 文件判斷
運算符 | 作用 |
---|---|
-e | 判斷對象是否存在 |
-d | 判斷對象是否存在,並且爲目錄 |
-f | 判斷對象是否存在,並且爲常規文件 |
-L | 判斷對象是否存在,並且爲符號鏈接 |
-h | 判斷對象是否存在,並且爲軟鏈接 |
-s | 判斷對象是否存在,並且長度不爲0 |
-r | 判斷對象是否存在,並且可讀 |
-w | 判斷對象是否存在,並且可寫 |
-x | 判斷對象是否存在,並且可執行 |
-O | 判斷對象是否存在,並且屬於當前用戶 |
-G | 判斷對象是否存在,並且屬於當前用戶組 |
-nt | 判斷file1是否比file2新 |
-ot | 判斷file1是否比file2舊 |
4.7 流程控制語句
4.7.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
輸出:
表達式結果不相等
4.7.2 for 語句
if語句格式如下:
for <var> in [list] ;do
# do something
done
例子:
read input #輸入: 1 2 3
for val in ${input}
do
echo "val:${val}" #輸出: val:1 val:2 val:3
done
4.7.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. 數組
5.1 定義和基本用法
shell中也有數組,數組的格式是用英文小括號元素包裹起來,元素與元素之前用若干個分割符隔開(空格,製表符,換行符),這個分割符定義在IFS
變量中,我們可以通過設置IFS變量自定義分隔符。來看看如何定義一個數組:
array=(1 2 3 4 5 'hello')
也可以定義一個空數組
array=()
訪問和修改數組元素的格式如下:
array[index]=value
和大多數編程語言一樣,shell的數組索引也是從0開始的,假如想要分別修改數組的第一個和第二個元素爲88和77:
array[0]=88
array[1]=77
上面的代碼,如果array爲空,88和77將被添加到數組中。
5.2 遍歷
遍歷數組太常見了,如果想對數組每個元素都進行特定的操作(訪問,修改)就需要遍歷。
在shell中,有兩個方式可以得到數組的全部元素,${array_name[*]}
和${array_name[@]}
,有了這個知識,我們就遍歷數組了
#!/bin/bash
array=("My favoriate number is" 65 22 )
idx=0
for elem in ${array[*]}
do
echo "Array element ${idx} is:${elem}"
idx=$(expr $idx + 1)
done
輸出:
Array element 0 is:My
Array element 1 is:favoriate
Array element 2 is:number
Array element 3 is:is
Array element 4 is:65
Array element 5 is:22
在上面的代碼中我們可能以爲自己定義了一個字符串和兩個數字在數組中,應該打印出一行字符串和兩個數字。但是卻不是這樣的,只要有空白符,shell會把它們當成數組的分隔符,這些被隔開的部分就會被當成數組的元素。
6. 函數
- 用function關鍵字function fun() 定義一個函數,也可以直接fun() 定義,不帶任何參數。
- 函數有參數,調用時,在函數名後面寫上參數,多個參數用空格隔開
- 調用函數時傳遞參數,在函數體內部,通過n的形式獲取參數的值,$1表示第1個參數,$2表示第2個參數...
- 參數返回,可以顯示加:return 返回。如果不加,將以最後一條命令運行結果,作爲返回值。 return後跟數值n(0-255
結構:
function funcName(){
#do something
return ${bianliang}
}
#或者
funcName(){
#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.
7. 重定向
分輸入重定向和輸出重定向;從字面上理解,輸入輸出重定向就是「改變輸入與輸出的方向」的意思。輸入輸出方向就是數據的流動方向:
- 輸入方向就是數據從哪裏流向程序。數據默認從鍵盤流向程序,如果改變了它的方向,數據就從其它地方流入,這就是輸入重定向。
- 輸出方向就是數據從程序流向哪裏。數據默認從程序流向顯示器,如果改變了它的方向,數據就流向其它地方,這就是輸出重定向。
重定向符 | 作用 |
---|---|
command>file | 以覆蓋的方式,把 command 的正確輸出結果輸出到 file 文件中 |
command>>file | 以追加的方式,把 command 的正確輸出結果輸出到 file 文件中。 |
input<file | 將 file 文件中的內容作爲 command 的輸入。 |
command <<END | 從標準輸入(鍵盤)中讀取數據,直到遇見分界符 END 才停止(分界符可是任意的字符串,用戶自定義)。 |
command <file1 >file2 | 將 file1 作爲 command 的輸入,並將 command 的處理結果輸出到 file2。 |
例子:
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
執行上面的代碼,將錯誤流重定向到輸出流,這種做法在某些場合是很有用的。
7.2 特殊文件
7.2.1 /dev/null
所有重定向到這個文件的內容都會消失,常常同於忽略錯誤輸出。
ls /luoye 2> /dev/null
如果不存在/luoye這個目錄或者文件,就沒有什麼提示
7.2.2 /dev/zero
這個文件會不斷產出空的數據,該文件常被dd
命令使用
dd if=/dev/zero of=out.txt bs=1 count=16
從/dev/zero
輸入,輸出到out.txt
,生成一個大小爲16字節的空文件
8. 管道
管道是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