Shell編程的究極系列(補充語法)

基於上一篇的基礎語法,我來寫寫一些更進一層的語法規則,也是咱們平時若是要進行Shell編程的話,常用的點,其中包括這幾個話題:數組、雙小括號的算術運算、函數、退出碼,篇幅相較上一篇來講不長。程序員

1、數組

數組,在shell中操控起來比較「所見即所得」的感受,沒有一堆要記住的api接口,頂多就是三四個語法上的輔助。對於數組,基於Bash4.0以後分爲:基本數據和關聯數組。關聯數組有點像咱們Java中的Map,不過功能就簡單多了。正則表達式

一、基本數組

直接上代碼:shell

#!/bin/bash


var=test4
# 定義一個簡單的基本數組,能夠發現有多種賦值的方式
array_var=(test1 'test2' "test3" "$var")

# 這種只能輸出數組的第一個位置的數據
echo $array_var

# 注意這種只能輸出test1[1],後面的索引定位並無生效
echo $array_var[1]

# 這種,使用大括號將數組和索引括起來的語法,才能使索引生效,取到數組具體位置上面的值
echo ${array_var[1]}

# 這兩種均可以將數組打印出來
echo ${array_var[*]}
echo ${array_var[@]}

# 這種能夠打印數組的個數
echo ${#array_var[*]}

# 刪除某個索引位置上面的值
unset array_var[1]

<<COMMENT
輸出結果:
test1
test1[1]
test2
test1 test2 test3 test4
test1 test2 test3 test4
4
COMMENT

a、多種定義方式

好幾種定義數組並初始化的方式,下面來看:編程

#!/bin/bash

# 第一種
array_var=(
var1
var2
var3
)

# 第二種
array_var=(var1 var2 var3)

# 第三種
array_var[0]=var1
array_var[1]=var2
array_var[2]=var2

# 第四種
array_var=([0]=var1 [1]=var2 [3]=var3)

# 第五種
str="var1 var2 var3"
array_var=($str)

注意最後一種,是用來字符串轉數組的主要方式,用的不少flask

b、給數組添加元素的方式

兩種:api

#!/bin/bash

array=(var1 var2)

# 第一種
len=${#array[@]}
array[$len]=var3

# 第二種
array=(${array[@]} var3)

c、數組切片的方式

好幾種方式,但終歸都是使用一種語法的:數組

#!/bin/bash

array=(var1 var2 var3 var4 var5)

echo ${array[@]:1}#從第一個元素取到最後一個
echo ${array[@]:1:3}#從第一個,取三個元素
echo ${array[@]::3}#從第0個開始,取三個
echo ${array[@]:-1:3}#從倒數第一個開始,取三個

這裏要注意的是:最後一個分號後面的數字,是要取的數量,而不是結束的索引值bash

d、數組元素單詞替換方式

這種說實話,用的不多,不過很方便的一種語法糖,請看:模塊化

#!/bin/bash

array=(var1 vara2 varv3 vara4 varrr5)

echo ${array[@]/v/V} #最小匹配,只替換每一個元素裏面第一個匹配到的字符
echo ${array[@]//v/V} #最大匹配,替換每一個元素裏面全部匹配到的字符
echo ${array[@]/v/} #刪除每一個元素第一個匹配到的字符
echo ${array[@]//v/} #刪除每一個元素所有匹配到的字符
echo ${array[@]/#v/V} #從左到右,只替換每一個元素中最左邊匹配到的字符
echo ${array[@]/%r/R} #從右到左,只替換每一個元素中最右邊匹配到的字符(測試以後很差使!)
ehco ${array[@]#v*r} #最短匹配刪除每一個元素中的數據
ehco ${array[@]##v*r} #最長匹配刪除每一個元素中的數據

<<COMMENT
結果:
Var1 Vara2 Varv3 Vara4 Varrr5
Var1 Vara2 VarV3 Vara4 Varrr5
ar1 ara2 arv3 ara4 arrr5
ar1 ara2 ar3 ara4 arrr5
Var1 Vara2 Varv3 Vara4 Varrr5
var1 vara2 varv3 vara4 varrr5
1 a2 v3 a4 rr5
1 a2 v3 a4 5
COMMENT

二、關聯數組

這東西相似於一個映射關係,咱們能夠用任意的文本做爲數組索引,而普通數組中的索引只能是0,1,2,3……另外關聯數組必需要進行聲明,不然使用的仍是普通數組:函數

declare -A array

下面是使用的代碼:

#!/bin/bash
declare -A array

array=([index1]=val1 [index2]=val2)
echo ${array[index1]}
echo ${array[index2]}

array[index3]=varl3

# 獲取全部的值
echo ${array[@]}

# 獲取全部的鍵
echo ${!array[@]}

# 獲取長度
echo ${#array[@]}

注意後面 三個操做,對於普通數組來講,獲取全部鍵也能夠,可是結果是:0,1,2,3……

2、雙小括號的算術運算

這個系列的第一篇文章中,介紹了三種算術運算方式:expr、$[]和bc。這裏再來一個很是經常使用的,針對整數的計算,這種方式不管從效率仍是開發效率上來講,都是最佳的,是最容易讓程序員接受學習的方式:(())

一、支持的操做符

從上到下,,優先級遞減

  • ++、--
  • !、~
  • **
  • *、/、%
  • +、-
  • <<、>>
  • <、<=、>、>=
  • ==、!=
  • &
  • ^
  • |
  • &&
  • ||
  • =、+=、-=、*=、/=、%=、&=、^=、|=、<<=、>>=

二、一些零散的操做總結

直接上代碼:

#!/bin/bash

# 這種是會報錯的,由於單純的兩個小括號並不是語法規範。要配合$進行取值使用
echo ((8*7))

# 這種沒問題,但沒有輸出。這只是單純的使用雙小括號進行了計算,不用取值操做
((5+9))

# 這種也沒問題,仍是沒有輸出,由於也是隻是單純的計算了下,內部把結果值給了一個i變量
# 因爲自己也是沒有對雙小括號進行取值操做,因此也是不會語法出錯的
((i=4+9))
# 這時候i爲13,由於上面賦了值了
echo $i

# 這種就有語法錯誤了。由於要對雙小括號取值,而後給i進行賦值,但是取值操做要配合$使用
i=((4+91))

# 下面三種都是正確的使用雙小括號的方式
echo $((5+3))
echo $((i=5+3))
i=$((5+31))

# 雙小括號內部引用外部變量能夠不加$符號
i=$((i+9))

# 內部怎麼空格都不受語法的約束~放開了!
i=$((   9-   8))

# 注意優先級!內部是可使用邏輯運算的,爲真結果爲1,爲假結果爲0
echo $((i++**2&&3))

# 恩,沒錯,混合運算徹底沒問題(帶邏輯操做的混合運算)
echo $((4+9*(2-3)&&4<7))

#這種會報錯,由於雙小括號不支持浮點數,只是整數的運算
echo $((4.4*7))

可見,雙小括號很是的方便與靈活,相較於$[],好用的多~之後經常使用~

3、函數與退出碼

shell沒有面向對象,用途的關係也不須要面向對象,函數就成了最後最後的模塊化的「堡壘」。咱們先來講書退出碼這個東西,在shell中相當重要。

一、退出碼與$?

我總喜歡這麼口語化的叫這東西是退出碼,其實全稱叫:退出狀態碼。他表達的是一個腳本(或是一個函數)最後的那條運行的命令的執行結果:正確爲0,報錯爲非0的值。上篇章中說到的各類if、while、until等,內部的判斷條件語句,就是根據這個來的。系統規定,這個碼,是一個數字,不能超過256,最大255。對於這個碼的設置與獲取,有下面三種:

  • exit n:用於退出整個當前運行的shell腳本,並設置退出的狀態碼
  • return n:用於退出當前函數,設置當前函數的退出狀態碼
  • $?:用於獲取上面那一條命令(腳本、函數)執行的結果退出碼是多少

注意,一旦執行命令(腳本、函數)結束後,應該使用$?立刻獲取,不然執行了其餘命令以後,退出碼就會變成其餘命令的了,$?只能獲取最後一條執行的結果。

#!/bin/bash
  
ls
# 這裏是0,由於上面的ls執行結果正確
echo $?

ls ladjflaskjdf
# 這裏執行結果爲2,由於上面的ladjflaskjdf並非目錄,執行報錯
echo $?

exit 1
# 這裏不會執行了,由於上面的exit已經退出本次shell了,退出碼被設置成了1
echo "not exec"
exit 0

二、函數的定義與使用

定義很簡單,兩種方式:

# 第一種
function funal {
    commands
}
# 第二種
funal() {
 	commands   
}

其實,函數,咱們就把他當成一個小腳本,使用方式,幾乎同樣

a、基本使用

#!/bin/bash 

function func1 {
	echo "This is an example of a function" 
}

count=1
while [ $count -le 5 ] 
do
	func1
	count=$[ $count + 1 ] done

echo "This is the end of the loop"
func1
echo "Now this is the end of the script"

b、基本傳參

函數也能傳參,和咱們命令行中調用腳本如出一轍,函數內部使用參數也是一個樣子:

#!/bin/bash

function funcl {
	# 注意:這裏獲取到的仍是本shell腳本的名字,並非函數名
	echo "name is $0"
    echo $(($1+$2))
}
func1 23 45

c、數組傳參

一樣的,咱們可使用數組進行傳參

#!/bin/bash 

function addarray {
	# 這是一種局部變量的聲明方式,外部獲取不到,也能夠和全局變量重名而不受影響
	local sum=0
    local newarray
    newarray=($(echo "$@")) 
    for value in ${newarray[*]} 
    do
    	sum=$[ $sum + $value ]
    done 
	echo $sum

}
myarray=(1 2 3 4 5)
echo "The original array is: ${myarray[*]}"
# 加echo的緣由是,將本來的數組進行輸出。單獨${myarray[*]}是不會輸出的,也就沒辦法賦值
arg1=$(echo ${myarray[*]})
result=$(addarray $arg1)
echo "The result is $result"

d、退出狀態值

shell中的函數,除了執行完以後退出以外,可使用兩種方式退出:

  • exit:直接退出整個shell腳本了
  • return:退出當前函數

默認執行完以後,正常退出以後的狀態值是0。固然咱們可使用return語句,結束函數,並設置退出狀態值:

#!/bin/bash

function funl {
    if [ $1 -eq 0 ]
    then
    	echo "return 0"
    	# 使用return提早退出函數
    	return 0
    elif [$ -ne 0]
    then
    	echo "return 1"
    	return 1
    fi
    
}

funl 0
echo $?
funl 3
echo $?
<<COMMENT
結果:
return 0
0
return 1
1
COMMENT

e、返回值

和通常語言不一樣,shell尤其「精奇」!對於返回值這萬年函數的固有的"招數",居然不使用return關鍵字,這也是我使用、接觸、哪怕知道一點的全部計算機語言中,惟獨shell是這麼搞的!!!!!在shell中,函數的返回值,是函數內部全部的標準輸出,會使用空格分割,將全部的標準輸出組合成一個字符串,賦值給函數的結果引用變量,請看下面的:

#!/bin/bash
  
function funl {
	# 下面三個命令都是有標準輸出的!
    ls -al
    echo 12
    echo 34
}

# 這裏把函數的標準輸出都保存到了一個變量中
var=$(funl)
# 這種能夠把返回值變成一個數組
var_arr=($var)
echo $var
echo ${var_arr[0]}
echo ${var_arr[1]}
<<COMMENT
結果:
總用量 16 drwxrwxr-x 2 jicheng jicheng 4096 10月 19 15:20 . drwxrwxr-x 6 jicheng jicheng 4096 10月 16 10:35 .. -rw-rw-r-- 1 jicheng jicheng 58 10月 17 11:50 out -rwxrw-r-- 1 jicheng jicheng 183 10月 19 15:20 shell_test.sh 12 34
總用量
16
COMMENT

f、導入函數

若是想在其餘當前shell腳本中使用其餘文件中定義的函數,要使用source命令,而且source命令還可使用.來代替的語法糖,請看下面:

#!/bin/bash
# 在當前的shell腳本環境中,導入function_dict
source ./function_dict
# 這種是語法糖,通常使用這種,但要注意,點與後面的路徑有空格
. ./funtion_dict

三、函數使用

涉及到函數使用,那範圍很是廣了,我這裏只寫一種,那就是使用函數進行遞歸操做!遞歸也是程序員常常涉及到的,稍微有點思緒的結構。咱們來使用shell,寫一個求階層的函數:

#!/bin/bash

function factorial {
    if [ $1 -eq 1 ]
    then
    	echo 1
    else
    	local temp=$(( $1 - 1 ))
    	# 開始遞歸
    	local result=$(factorial $temp)
    	echo $(( $result * $1 ))
    fi
}
read -p "Enter value:" value
result=$(factorial $value)
echo "result is:$result"

4、結束語

又一堆語法補充。我從網上也各處google了下,感受shell的本質的語法點仍是有不少的,例如尚未講的一大塊:正則表達式~(心累~哎)下面一章,緊接着我想寫一下實際的shell腳本實戰,根據現有的語法+上一些簡單的命令,來構建一些有用的。在那裏我再講正則吧~預期以下

  • sed與正則
  • awk(gawk)
  • find
  • 。。。。。。。。
相關文章
相關標籤/搜索