最近把Linux Bash編程的知識複習了一遍,大概梳理了一下,作個記錄。html
LINUX shell的種類很是之多,可是目前用得最爲普遍的仍是Bash,本文也是基於Bash的Shell環境。 下面是一個簡單的示例:java
#! /bin/sh echo 'hello world!'
這就是一個最簡單的shell腳本了。 第一行的#!
用來告訴系統,這個腳本用什麼解釋器來執行(說明:sh和bash自己是不一樣的Shell,可是在我目前用得CentOS7版本sh和bash是等價的,sh是一個指向bash的符號連接)。 echo
命令輸出文本到屏幕程序員
一種方式就是將腳本做爲解釋器的參數,如:shell
sh test.sh
第二種方式就是授予文件可執行權限編程
chmod +x test.sh 或者 chmod 755 test.sh 執行腳本 ./test.sh
Bash是一種弱類型的語言,你只須要直接定義變量名=value
便可。當須要引用這個變量的時候使用$var_name
或者${var_name}
便可。 $var_name
是${var_name}
的一種簡寫形式,可是在某些狀況下只能使用${var_name}
,例如:數組
# 當須要鏈接兩個變量 your_id=${USER}-on-${HOSTNAME} # 或者會與其餘字符串寫在一塊兒的時候 echo '${num}instance running!'
因此建議在寫shell的時候統一使用${var_name}
的風格。 bash中變量是弱類型的,那麼無論你傳給他的是字符串、數字或者數組他都通通會接收,有時候這就會致使一些問題,好比你想作個計算器,這時候傳給了變量一個'ABCDEFG',那麼這時候咱們就想這個變量只能接收數字。又或者你想定義一個常量,不想用戶修改。那麼以上這些咱們可使用declare
來聲明變量的一些屬性。 declare經常使用參數以下:bash
-r 定義一個只讀變量,也可使用"readonly var_name"將變量定義爲只讀變量 -i 定義變量類型爲整型 -a 定義變量類型爲數組 -f 定義一個函數 -x 定義一個全局變量dom
當想刪除一個變量的時候直接使用unset var_name
便可,注意這個命令不能刪除只讀變量。編程語言
那麼如何向一個腳本傳遞參數呢? 當咱們想向一個腳本傳遞參數的時候,直接在執行腳本的命令後面跟參數便可:函數
sh ./test.sh parm1 parm2 parm3
在腳本中使用$n
(n是一個數字,如:$1獲取第一個參數)來接收傳入的參數便可。下面是可能常常用到的參數變量:
$0 當前腳本的文件名。 $n 傳遞給腳本或函數的參數。n是一個數字。例如,第一個參數是 $1 。 $# 傳遞給腳本或函數的參數個數。 $* 傳遞給腳本或函數的全部參數。 $@ 傳遞給腳本或函數的全部參數。被雙引號 (" ")包含時與$*不一樣。 $? 上個命令的退出狀態,或函數的返回值。 $_ 上一個命令的最後一個參數
咱們常常用到的Linux命令中有一種叫option的東西,基本模式-optionname
或者--longoptionname
。若是在咱們編寫腳本的時候須要option的話,簡單的腳本直接把他當普通參數手工處理便可,面對比較複雜的可使用getopt或者getopt。 這裏感受有必要提一下set
這個命令,我以爲很牛逼、很厲害、頗有用可是我歷來沒在腳本中使用過的命令。 set
命令用來改變內部腳本的變量或者選項。一種應用就是來改變options來影響腳本的行爲。另外一種應用就是利用set `commond`
的輸出來重置位置參數。
#!/bin/bash echo "Command-line argument #1 = $1" echo "Command-line argument #2 = $2" echo "Command-line argument #3 = $3" echo "--------------------------------------" echo "利用`uname -a`的輸出來重置位置參數" set `uname -a` echo "Field #1 of 'uname -a' = $1" echo "Field #2 of 'uname -a' = $2" echo "Field #3 of 'uname -a' = $3" exit 0 output: [root@localhost study]# sh test_set.sh one two three Command-line argument #1 = one Command-line argument #2 = two Command-line argument #3 = three -------------------------------------- Sets the positional parameters to the output of the commond `uname -a` Field #1 of 'uname -a' = Linux Field #2 of 'uname -a' = localhost.localdomain Field #3 of 'uname -a' = 3.10.0-514.el7.x86_64
目前只支持一維數組!!!!!!!!!! 能夠經過如下兩種方式來爲數組賦值:
#!/bin/bash my_array=(A B C "D") my_array[4]=F my_array[5]="G"
經過${my_array[idx]}
來讀取數組中的值,也能夠經過${my_array[*]}
或${my_array[@]}
來一次性讀取數組中的全部元素。經過${#my_array[*]}
或${#my_array[@]}
來獲取數組長度。
#!/bin/bash my_array=(A B C "D") my_array[4]=F my_array[5]="G123" echo "第一個元素:${my_array[0]}" echo "經過*來獲取數組全部元素:${my_array[*]}" echo "經過@來獲取數組全部元素:${my_array[@]}" echo "經過*來獲取數組長度:${#my_array[*]}" echo "經過@來獲取數組長度:${#my_array[@]}" output: [root@localhost study]# sh test_arr.sh 第一個元素:A 經過*來獲取數組全部元素:A B C D F G123 經過@來獲取數組全部元素:A B C D F G123 經過*來獲取數組長度:6 經過@來獲取數組長度:6
反引號括起來的字符串被bash解釋爲命令行執行,並以它的輸出結果取代整個反引號部分。
#! /bin/sh str=`echo $PATH` echo ${str} output: [root@localhost study]# sh qut.sh /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
單引號擴起來的字符都視做普通字符,全部特殊字符都會失去原油的意義。
#! /bin/sh str='${PATH}' echo ${str} output: [root@localhost study]# sh qut.sh ${PATH}
由雙引號括起來的字符,除$、\、’、和」這幾個字符還是特殊字符並保留其特殊功能外,其他字符做爲普通字符對待。
for循環的通常格式爲
for var in [list] do commonds done
下面來看個例子:
#!/bin/bash # 若是後面的list不加引號,則默認以空格分割 for var in A B C D do echo "${var}" done echo;echo # 會將引號內的內容當作一個元素 for var in "A A" "B B B" "C C" do echo "${var}" done echo;echo # 上面兩種寫法有點相似java的for-each # 若是你用過C,那麼你對下面這種寫法更熟悉 for ((a=1, b=1; a <= 5 ; a++, b++)) do echo -n "$a-$b " done output: [root@localhost study]# sh for-loop.sh A B C D A A B B B C C 1-1 2-2 3-3 4-4 5-5
while循環的通常格式爲
while [ condition ] do commands done
Example:
#!/bin/bash while (( a <= 5 )) do echo -n "$a " let "a+=1" done
break用來跳出整個循環,continue跳出本次循環。
首先來看個例子:
#! /bin/bash b=1+2 echo "${b}" output: [root@localhost study]# sh test.sh 1+2
默認狀況下,bash中會把全部的賦值當作字符或字符串,因此看到的輸出是1+2
而不是3
。 能夠用如下三種方式來作算術運算。
#! /bin/bash # 一、使用`declare`來聲明變量爲整型。 declare -i b b=1+2 echo "declare:${b}" echo;echo # 二、let命令 let "c=1+1" echo "let:${c}" echo;echo # 三、利用雙括號將表達式括起來 ((d=2+3)) echo "((表達式)):${d}" echo;echo # 四、利用單個方括號 echo "[表達式]:$[1+2]" output: [root@localhost study]# sh test.sh declare:3 let:2 ((表達式)):5 [表達式]:3
首先須要注意的是在Linux中「0」表示成功。test命令用來檢查某個條件是否成立,它能夠進行數值、字符和文件三個方面的測試。
test
這個命令Example:
[root@localhost ~]# test 1 -lt 2 [root@localhost ~]# echo $? 0 [root@localhost ~]# [ 1 -lt 2 ] [root@localhost ~]# echo $? 0 [root@localhost ~]# [[ 2 -eq 2 ]] [root@localhost ~]# echo $? 0 [root@localhost ~]# (( 2 == 2 )) [root@localhost ~]# echo $? 0
數值測試操做符:
Example:
#! /bin/sh a=4 b=5 if [ ${a} -ne ${b} ] then echo "${a} is not equal to ${b} fi echo if [ ${a} != ${b} ] then echo "${a} is not equal to ${b}." fi
: 大於,根據ASCII的排序
Example:
#! /bin/sh str1="" if [ -z ${str1} ] then echo "str1 is null" fi echo str1="abc" if [ -n ${str1} ] then echo "str1 is not null" fi echo str2="cdef" if [ "${str1}" != "${str2}" ] then echo "${str1} is not equal to ${str2}" fi
下面列舉一些經常使用的文件操做:
Example:
#! /bin/sh filename="/study/bash/test_file.txt" if [ -e "${filename}" ] then echo "${filename} file exists." fi if [ -f "${filename}" ] then echo "${filename} is a regular file." fi if [ -s "${filename}" ] then echo "${filename} not a zero file." fi
if語句的通常格式:
if condition then commonds fi
if-else語句的格式
if condition1 then commonds elif condition2 then commonds else commonds fi
case語句的通常格式:
case 值 in 模式1) commonds ;; 模式2) commonds ;; esac
在case語句中能夠用*
號來匹配任意值。
平時在一些簡單的腳本里面基本用不到函數,可是在複雜的腳本中用來組織代碼以及封裝經常使用邏輯,在bash中仍是很好用的東西。 函數的定義格式以下:
[ function ] funname [()]{ action; [return ${result};] }
一、咱們能夠function funname
方式來定義,也能夠直接funname()
。 二、能夠顯示的加return來返回結果或者直接返回最後一條命令的執行結果。
Example:
#! /bin/sh fun(){ echo "it's a function" } fun
向函數傳遞參數的形式相似執行腳本的傳參:fun_name arg1 arg2
Example:
#! /bin/sh fun(){ if [ ${1} -gt ${2} ] then echo "${1} bigger than ${2}." elif [ ${2} -gt ${1} ] then echo "${2} bigger than ${1}." else echo "${1} equal to ${2}." fi } fun 10 20
能夠經過read
命令來讀取標準輸入的值。下面用一個例子來看下在bash中read
的經常使用方式:
#! /bin/sh # 將讀取到的值賦值給var1這個變量,當咱們在行尾加\時,按回車會換到下一行繼續輸入 read var1 echo "var1 is ${var1}" echo "===============================" # 直接read,可使用$REPLY來獲取讀取到的值 read echo "read $REPLY" echo "===============================" # -r參數會按字符串解釋\,而不會換行 read -r mul_line echo "mul_line:${mul_line}" echo "===============================" # 上面的方式都會在屏幕上打印輸入值,在交互式的bash中是不友好的,咱們能夠加-s選項來屏蔽輸入的打印 read -s var2 echo "dont echo input:${var2}" echo "===============================" # 有時候可能不但願一直等待用戶輸入,咱們能夠用-t選項來這隻超時時間 TIMELIMIT=5 read -t $TIMELIMIT var3 if [ -z ${var3}] then var3="time out" fi echo "timed read:${var3}"
下面是測試的輸出
[root@localhost study]# sh read_test.sh read value to var2\ new line var1 is read value to var2new line =============================== read had no var read read had no var =============================== read with -r option\ mul_line:read with -r option\ =============================== dont echo input:do not echo input =============================== timed read:time out
在大多數編程語言中,字符串是很是重要、經常使用的數據類型。 一、字符串長度
${#string}
expr length $string
expr "$string" : '.*'
Example:
#! /bin/sh str1="fdhjksdf" echo "${#str1}" echo "`expr length ${str1}`" echo "`expr "${str1}" : '.*'`"
二、匹配子串長度
expr match "$string" '$substr_regxp'
expr "$string" : '$substr_regxp'
Example:
#!/bin/sh str1="str;str-str;strend" sub_regxp="[a-z]*;" echo "`expr match "${str1}" "${sub_regxp}"`" echo "`expr "${str1}" : "${sub_regxp}"`"
三、子串開始索引
expr index $string $substring
Example
#! /bin/sh str="abc;efg;higj" rgx_str=";.*;" echo "`expr index ${str} ${rgx_str}`"
四、字符串截取
${string:position}
截取從postition位置開始的字符串${string:position:length}
從position位置開始截取長度爲length長的子串,postition的開始索引爲0expr substr $string $position $length
從position位置開始截取長度爲length長的子串,postition的開始索引爲1expr match "$string" '$sub_regxp'
截取匹配正則的子串expr "$string" : '$sub_regxp'
截取匹配正則的子串
Example
#! /bin/sh str="abc;defg;hijk;lmnpq" echo "\${string:position}" echo "${str:4}" echo "===================================" echo "\${string:position:length}" echo "${str:4:4}" echo "===================================" echo "expr substr \$string \$position \$length" echo "`expr substr ${str} 5 4`" echo "===================================" sub_regxp=".*;\(.*\);" echo "expr match "\$string" '${sub_regxp}'" echo "`expr match "${str}" ${sub_regxp}`" echo "===================================" echo "expr "\$string" : '\$sub_regxp'" echo "`expr "${str}" : ${sub_regxp}`" output: [root@localhost study]# sh sub_str.sh ${string:position} defg;hijk;lmnpq =================================== ${string:position:length} defg =================================== expr substr $string $position $length defg =================================== expr match $string '.*;\(.*\);' hijk =================================== expr $string : '$sub_regxp' hijk
五、刪除子串
${string#substring}
,從前面最小匹配刪除${string##substring}
,從前面最長匹配刪除${string%substring}
,從後面最小匹配刪除${string%%substring}
,從後面最小匹配刪除
Example
#! /bin/sh str=abcABC123ABCabc reg_str='a*C' echo ${str#$reg_str} # 123ABCabc echo ${str##$reg_str} # abc reg_str='b*c' echo ${str%$reg_str} # abcABC123ABCa echo ${str%%$reg_str} # a
六、子串替換
${string/substring/replacement}
從前面最小匹配替換${string//substring/replacement}
從前面最長匹配替換${string/#substring/replacement}
從後面最小匹配替換${string/%substring/replacement}
從後面最長匹配替換
在bash中提供了相關的參數供咱們進行腳本的調試追蹤。
sh [-nvx] script.sh 參數: -n:不執行基本,只檢查語法問題 -v:執行腳本前,先將腳本內容輸出到屏幕上 -x:將使用到的內容顯示到屏幕上