- 前言
- Shell程式設計
- bash特性
- shell作用域
- 變數
- 環境變數
- $特殊變數
- $特殊狀態變數
- $特殊符號(很重要)
- 其他內建shell命令
- shell語法的子串擷取
- 統計
- 指令執行時間
- 練習
- shell特殊擴充套件變數
- 父子shell的理解
- 內建和外接命令
- 區別
- 數值計算
- 雙括號(())運算
- let
- expr
- expr模式匹配
- bc
- awk
- 中括號
- shell的條件判斷
- test判斷
- 中括號[]判斷
- 中括號寫判斷符號
- 雙中括號[[]]
- if分支
- case
- for
- while
- 普通陣列
- 函式
- 函式定義
- 函式執行
- 函式傳參
- 如何分檔案
- 規範
前言
本篇文章就是一個過渡學習的,先入門shell指令碼,由於作者有程式設計基礎,所以有些解釋的比較少。由於現在還在努力學習中,以後等本散修進階了之後再寫進階的、與網路安全更加貼合的shell程式設計
Shell程式設計
指定shebang的意思是指定指令碼執行的直譯器
#!/bin/bash #指定bash執行,那這就是shell語言指令碼了
#!/bin/python #指定python直譯器,那麼這個就是python語言指令碼
#!/bin/perl #指定perl直譯器,那這個指令碼就是perl語言指令碼
bash特性
檢視歷史指令最大儲存條數
echo $HISTSIZE
存放使用者執行的歷史指令的檔案
echo $HISTFILE
快速檢視歷史指令
!1000 #檢視第1000條歷史指令是什麼
!! #執行上一條指令
反引號執行指令
`ls` #反引號中的字元會當成指令執行
檔案是否能執行得看檔案是否具有x執行許可權
ll filename #檢視檔案屬性
chmod +x filename #新增執行許可權 這樣直接+x就是a+x
有執行許可權的檔案通常是標誌綠色的
執行shell指令碼方式
source shell.sh
. shell.sh
./shell.sh
bash shell.sh
sh shell.sh
source和.是在當前環境中載入變數,所以你使用這兩個命令載入shell指令碼後,裡面的變數就會載入到你當前的bash中,而不會開啟子shell,所以慎用這兩個命令。(這兩個source和.符號,在後面函式分檔案中也會用到)
shell作用域
每個使用者家目錄下都有一個自己的變數檔案,是在系統載入的時候載入
.bash_profile
該檔案作用是先載入.bashrc
然後再載入你下面定義的變數等等
這裡提前說明一下如何載入,注意是載入不是執行
source filename
. filename
以上兩個都是載入檔案的操作,這兩個方法都是可以在該檔案沒有x執行許可權的時候進行載入
因為如果要按照shell指令碼進行載入的話,那麼你裡面的變數都會變成區域性變數,那麼指令碼執行雖然也載入了,
但是執行完成後就會被釋放,出來後依然還是沒有成功載入你的變數
linux中執行shell指令碼方式
./shell.sh
bash shell.sh
sh shell.sh
bash和sh單獨使用的話,就是進入一個子shell中,我們可以透過pstree檢視。
可以透過exit退出子shell
安裝一個psmisc,主要用pstree功能來理解作用域,沒有也無所謂
yum install psmisc -y
- 下面是透過bash開第一個子shell,透過pstree可以看到確實是進入了一個子shell中
- 下面是透過sh進入的一個子shell,透過pstree可以看到確實是進入了一個子shell中
檢視一下兩個shell,pstree可以看到是不同bash的。
我們隨便定義一個變數,可以看到不同shell之間的變數是互通不了的,原因作用域的問題
作用域大概如下,age一般定義在自己bash中的變數是不能互通的:
每一個盒子都是一個shell盒子,他們之間的變數都是不能互通的
不管是他們的全域性變數還是區域性變數都是不可以互通
但是除非你定義在了系統變數,比如/etc/profile這種檔案中,
這種檔案是在系統載入的時候載入的檔案,所以每一個使用者都會生效。
但是除非你自己盒子裡面有和你在系統檔案中定義的變數,就會被覆蓋掉,以你自身的.bash_profile檔案為準
總結:
shell的作用域說白了就是每一個shell登入進來都是不一樣作用域,你要全域性生效就去修改/etc/profile下的系統載入檔案,但是你修改了不會立刻生效,你可以使用source /etc/profile 和 . /etc/profile就能夠載入一下該指令碼檔案,你的變數就會在你當前shell指令碼生效,但是其他已經登入進來的使用者不會生效,因為他沒有載入系統檔案,除非他退出重新登入或者source /etc/profile 或者 ./etc/profile才能載入生效。
變數
Linux 系統中環境變數約定好都是大寫,不容易和我們自己定義的出現衝突,也就是說我們定義的變數最好是小寫的
環境變數
檢視變數相關指令
set #輸出所有變數,包括全域性變數和區域性變數
env 只顯示全域性變數
declare 同set
export 顯示還有設定環境變數值,在linux命令中直接使用的話是臨時設定的哈
常見的環境變數
PATH:shell會到該變數定義的目錄中找命令和程式
PS1:基本提示符,對於 root 使用者是 #,對於普通使用者是 $
HISTSIZE:最大儲存的歷史記錄數
SHELL:該使用者使用的Shell,通常是/bin/bash
HOME:當前使用者給的預設主目錄
LANG:語言相關的環境變數,多語言可以修改此環境變數
USER:使用者名稱
LOGNAME:使用者的登入名
HOSTNAME:主機的名稱
MAIL:當前使用者的郵件存放目錄
撤銷環境變數名
- unset 變數名,刪除變數或者函式
設定只讀變數
-
readonly ,只有shell結束才能失效的變數
readonly name='abc' name=123 #企圖修改的時候就會報錯下面的資訊 -bash: name: 只讀變數
set 檢視系統中所有的系統變數
透過下圖可以看到,我們每一個shell就算定義在了profile檔案中,我們也要進行載入才能看到,就算我們直接grep該檔案也是檢視不到另一個shell中新增到profile中的變數,一定一定要使用source或者.符號進行載入才可以載入。
可以看到我們載入後就能夠看到和使用另一個shell新增進去的name變數了。
透過上面案例後,我們可以理解成:
set 就是檢視系統中定義的全域性變數還有區域性變數,不用管他是檢視哪個檔案。
(PS:env就是隻能看全域性,比如我們使用者自己在shell命令終端中定義的變數只有set能看,env看不到。)
其他檢視的型別不用瞭解太深,其實shell指令碼用到的大多都是自己定的變數,更多變數以後就會慢慢接觸到。(說人話:我懶得查了)
$特殊變數
$0 獲取shell指令碼的檔名以及指令碼路徑
$n 獲取shell指令碼執行的時候傳入的引數,比如$1就是獲取傳進來的第一個引數
$# 獲取執行的shell指令碼後面的引數總個數,這個是統計引數個數的變數
$* 獲取shell指令碼傳進來的所有引數,直接使用等於"$1" "$2" "$3"
"$*" 這樣加上引號是獲取所有引數,但是不是列表形式,而是整體"$1 $2 $3"
$@ 和 "$@" 都等於 $* ,他都是列表形式讓你迴圈
如下指令碼輸出,看完就能理解這幾個的區別了。
#!/bin/bash
echo '=====$@===line========='
for var in "$*"
do
echo "$var"
done
echo '=====$@===line========='
for var in $@
do
echo "$var"
done
echo '==="$@"=====line========='
for var in "$@"
do
echo "$var"
done
$特殊狀態變數
$? 上一次命令執行狀態返回值,0表示正確,非零表示失敗 #這裡終於明白了為什麼一開始學c語言的時候return 0是正常退出了!!!淚目!!
$$ 當前shell指令碼的程序號
$! 上一次後臺程序的PID
$_ 上次執行的命令的最後一個引數
更詳細更多的特殊狀態變數解釋可以直接檢視man手冊
man bash
$特殊符號(很重要)
${變數名} #取出變數的值
$變數名 #取出變數值
$(命令) #括號中執行命令,然後返回結果
`` #反引號中執行命令,然後返回結果
() #開啟子shell執行命令
其他內建shell命令
內建的shell有很多,但其實在學習Linux的時候就已經有學過一些了。比如:alias也是內建的shell命令
echo
printf
eval
exec
read
echo
-n 不換行輸出
-e 解析字串彙總的特殊符號,
比如:
\n 換行
\r 回車
\t 製表符(四個空格)
\b 退格
printf
echo需要加上-e才能識別特殊符號,那printf就是可以直接解析特殊符號的。
printf "hello\nworld\n"
eval
執行多個命令
eval ls;cd /tmp;ls
其實在linux中只使用分號也能實現多個命令執行。
這裡eval可能是為了在shell指令碼中好執行??我也不太懂,以後再深入瞭解。。。
exec
不建立子程序然後執行你給的命令,然後執行結束後就直接退出當前shell,說白了就是exec執行命令後就會自動exit你當前的shell
read
read [-引數] [變數1 變數2 ...]
注意:變數1 2 這些是輸入的資料,然後按照順序給到變數,變數名可以隨便起,後面要使用該變數的時候按照正常的$符號取變數值即可。
-p #顯示提示資訊,後面加上你要提示的字串
-r #不對輸入的字元進行轉義,讀到什麼字串就輸出什麼字串
-t #限制使用者輸入的時間,單位秒
-s #靜默模式,不會顯示你輸入的內容,就像你修改密碼的時候也不會顯示出來
-
-p引數解釋
read -p '請輸入資料:' name age echo $name echo $age 指令碼讀到read這行的時候就會要求使用者輸入資料,然後資料會按照順序給到變數name和age
-
-t引數解釋
read -t 5 -p "請輸入你要修改的名字:" name echo $name 指令碼讀到read這行的時候就會要求使用者輸入資料,然後資料給到name變數
shell語法的子串擷取
這裡使用name作為變數,下面都使用name
${name} 返回變數值
${#name} 返回name的字元長度(這個獲取長度命令很快)
${name:start} 返回name變數的start下標開始以及後面的字元
${name:start:length} 返回name變數的start下標開始然後擷取length個字元
下面介紹刪除字元的
str_or_pattern意思是可以是字元也可以是正則匹配模式,最短和最長就是因為有正則模糊匹配
${name#str_or_pattern} 從變數開頭開始刪除匹配最短str_or_pattern
${name##str_or_pattern} 從變數開頭開始刪除匹配最短的str_or_pattern
${name%str_or_pattern} 從變數結尾開始刪除匹配最短的str_or_pattern
${name%%str_or_pattern} 從變數結尾開始刪除匹配最長的str_or_pattern
下面介紹替換字元的
下面這種方式有點像我們sed還有vim裡面自帶的字元替換,所以說Linux語法都是大差不差的哈
${name/pattern/string} 用string替換第一個匹配的pattern
${name//pattern/string} 用string替換所有匹配的pattern
-
注意
#上面的刪除字元#和%這兩個,#開頭一定要匹配上,比如name=123abcABC456abcABC #那麼我們要刪除的話肯定是1開頭的,如果不是1開頭就無效,給你返回源字元了 [root@localhost ~]# echo ${name#1*c} ABC456abcABC [root@localhost ~]# echo ${name##1*c} ABC [root@localhost ~]# echo ${name##2*c} #匹配不正確導致的輸出原變數值 123abcABC456abcABC #那麼同理%也是,只不過是從結尾開始匹配,那麼我們給的str_or_pattern結尾要和變數最後一個字元一樣才行哈 [root@localhost ~]# echo ${name%b*C} 123abcABC456a [root@localhost ~]# echo ${name%%b*C} 123a [root@localhost ~]# echo ${name%%C*a} #匹配不正確導致的輸出原變數值 123abcABC456abcABC
統計
-
最快統計字元方式就是自帶的
echo ${#name}
-
wc
-L 統計字元數最多那一行,輸出他的字元數量 -l 統計檔案行數 -c 統計檔案字元數
-
expr length "字元"
直接輸出字元長度,這裡可以使用變數,當然你用變數的話記得使用雙引號 expr length "${name}"
指令執行時間
計算方式:time 指令
比如我們這裡統計上面統計字元長度哪個指令是速度最快的
待檢測資料為:
time for i in {1..10000};do str=`seq -s "ok" 10`;echo ${#str} &> /dev/null;done
修改${#str}部分為其他統計字元指令即可檢測不同指令之間的效率
知識點:
&>是1&2>的縮寫
&1是為了標識1不是一個檔案,而是一個標準輸出,所以 2>&1意思是將標準錯誤輸入到標準輸出1中
command &> /dev/null 等於 command > /dev/null 2>&1
-
${#str} 這種方式最快
[root@localhost ~]# time for i in {1..10000};do str=`seq -s "ok" 10`;echo ${#str} &> /dev/null;done real 0m10.319s user 0m4.307s sys 0m6.660s
-
wc -L 需要使用到管道符一般都比較慢
[root@localhost ~]# time for i in {1..10000};do str=`seq -s "ok" 10`;echo $str | wc -L &> /dev/null;done real 0m19.679s user 0m12.981s sys 0m13.072s
-
expr length "${str}"
[root@localhost ~]# time for i in {1..10000};do str=`seq -s "ok" 10`;expr length "${str}" &> /dev/null;done real 0m19.057s user 0m8.865s sys 0m11.688s
練習
資料
[root@localhost ~]# touch whoisdhan_{1..10}_nono
[root@localhost ~]#
[root@localhost ~]# ls who*
whoisdhan_10_nono whoisdhan_4_nono whoisdhan_8_nono
whoisdhan_1_nono whoisdhan_5_nono whoisdhan_9_nono
whoisdhan_2_nono whoisdhan_6_nono
whoisdhan_3_nono whoisdhan_7_nono
將上面的檔案所有帶_nono的,都替換為空,比如whoisdhan_10_nono 變成 whoisdhan_10
答案不止一個,下面是我的答案
for var in `ls who*`;do mv $var ${var//_nono/};done
#從這裡可以擴充套件一下思維。我第一時間想到的是find命令進行查詢,其實不用,因為我們的檔案就在該目錄下了,直接ls萬用字元模糊匹配即可。
shell特殊擴充套件變數
注意:這裡我個人認為是適合用在開發指令碼的時候,對指令碼接受到的引數進行篩選甄別。
${parameter:-str} #如果parameter為空的時候,就返回str字串,不為空那就返回parameter
${parameter:=str} #如果parameter為空的時候,將parameter賦值為str並且返回str值
${parameter:?str} #如果parameter為空的時候,將str當作錯誤內容輸出,否則輸出parameter值
就像下面這樣
[root@localhost ~]# echo ${q:?空變數}
-bash: q: 空變數
${parameter:+str} #parameter為空的時候啥也不做,如果不為空那就返回str(可以用來判斷某些引數是否為空)
就像下面這樣
[root@localhost ~]# echo ${name:+不為空}
不為空
父子shell的理解
下面是我看超哥教程的一份圖解:
為什麼要理解父子shell的關係,原因是因為我們shell程式設計中一個括號就能夠開啟一個子shell,目的是不讓某些操作比如Ping指令將我們當前的shell卡主,這樣的話你可以開一個子shell去執行ping這種指令,然後可以讓shell指令碼繼續執行下去。
$BASH_SUBSHELL該變數是檢測開啟了幾個子shell
(cd ~;pwd;ls;(cd /tmp;ls;(echo "開啟了:$BASH_SUBSHELL 個子shell")))
內建和外接命令
識別內建還是外接
type cd
type rename
快速檢視系統有哪些是內建命令
compgen -b
區別
先認識一下兩個的意思
-
內建
在系統啟動時就載入記憶體,常駐記憶體,執行效率更高,但是佔用資源
-
外接
系統需要從硬碟中讀取程式檔案,再讀入記憶體載入
二者區別:
內建就是不開啟子程序,直接在當前shell中執行,外接就是需要下載的一些系統命令,使用外接命令的時候需要開啟一個子程序執行。
數值計算
雙括號(())運算
((i=i+1)) #雙括號裡面進行一系列的運算
$((i=i+1)) #加個$符號就是取你計算出來的結果
((條件判斷)) #為真返回1,為假返回0
注意:
假設你有一個變數名為name=123
當你使用雙括號進行name變數運算的時候是按照你name本來的值進行運算的,同時可以修改name的值。
((name++))
echo $name
124
計算器簡易版
#!/bin/bash
echo $(($1))
let
age=5
let age+=5 #let後面就能夠直接使用變數進行計算了
等同於
$((age+=5)) #但是這種是會報錯的,因為會把結果10當成命令執行,但是我們賦值是賦值成功了的
expr
複習一下,之前我們使用expr進行字元長度計算
直接輸出字元長度,這裡可以使用變數,當然你用變數的話記得使用雙引號
expr length "${name}"
expr --help
將表示式的值列印到標準輸出,分隔符下面的空行可提升算式優先順序。
可用的表示式有:
ARG1 | ARG2 若ARG1 的值不為0 或者為空,則返回ARG1,否則返回ARG2
ARG1 & ARG2 若兩邊的值都不為0 或為空,則返回ARG1,否則返回 0
ARG1 < ARG2 ARG1 小於ARG2
ARG1 <= ARG2 ARG1 小於或等於ARG2
ARG1 = ARG2 ARG1 等於ARG2
ARG1 != ARG2 ARG1 不等於ARG2
ARG1 >= ARG2 ARG1 大於或等於ARG2
ARG1 > ARG2 ARG1 大於ARG2
ARG1 + ARG2 計算 ARG1 與ARG2 相加之和
ARG1 - ARG2 計算 ARG1 與ARG2 相減之差
ARG1 * ARG2 計算 ARG1 與ARG2 相乘之積
ARG1 / ARG2 計算 ARG1 與ARG2 相除之商
ARG1 % ARG2 計算 ARG1 與ARG2 相除之餘數
字串 : 表示式 定位字串中匹配表示式的模式
match 字串 表示式 等於"字串 :表示式"
substr 字串 偏移量 長度 替換字串的子串,偏移的數值從 1 起計
index 字串 字元 在字串中發現字元的地方建立下標,或者標0
length 字串 字串的長度
在使用expr進行計算的時候,符號與資料之間記得加上空格才能夠識別成功過,和變數賦值不一樣哈
加減乘除
expr 2 + 3
expr 2 - 3
expr 2 \* 3 #乘法這裡要對乘號進行字元轉義
expr 2 / 3
判斷符號
expr 1 \> 2
expr 1 \< 2
expr模式匹配
: 冒號,計算字串的數量
.* 任意字串重複0或多次
看一個語法就理解了(就是有點奇怪,用冒號作為計算字串標誌)
str=str.aaa.bbb
expr $str ":" "st.*b"
11
意思是計算出str.aaa.bbb字元一共11個字元數量
"st.*b"這個是正則匹配哈,特殊符號記得加轉移\
說白了expr就是正則模式匹配上了的,就統計你那個匹配上的字元,但是expr匹配模式是從字串開始匹配的,所以只能規定後面的截止字元,
比如expr str.abc.abwwwc ":" "a*c"這樣是匹配不成功的,因為字串是s開頭,而你給的匹配模式是a開頭。
bc
bc計算器
bc是可以直接當作計算機使用的。你直接敲bc命令進入後,直接輸入加減乘除這些式子回車都能夠給你輸出接過來。
[root@localhost ~]# bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
1+1
2
(1+2)*3
9
9*9
81
透過管道符計算
echo "4*4" | bc
echo '1+1' | bc
[root@localhost ~]# echo '9*3' | bc
27
[root@localhost ~]# echo "1+2*3" | bc
7
練習:計算1..100的總和
方式一:echo {1..100} | tr ' ' '+' | bc
方式二:seq -s '+' 100 | bc
方式三:seq -s ' + ' 100 | xargs expr
方式三我的理解:首先xargs不加-i引數是因為-i是要配合{}一起使用,然後是一個一個的傳進去,但是expr是expr 1 + 2這樣的,所以我們就不能用-i,那麼我們也不能直接 seq -s ' + ' 100 | expr直接管道符接,因為相當於字串過去了,即相當於expr "1 + 2 + ..",那我們要配合xargs這種是接到"1 + 2 + .."後,當引數給到exrp而不是整個字串給他,那麼就剛好等於expr 1 + 2 + 3 + ..
awk
知道這種格式用法即可了
echo "2 3" | awk '{print($1*$2)}' #等於2*3=6
中括號
num=3
$[num+=2]
$[num-1]
[root@localhost ~]# num=3
[root@localhost ~]# res=$[num+=5]
[root@localhost ~]# echo $res
8
需要注意的是,不要直接就是$[num=3]這樣,因為你這樣就相當於執行了一個$[num=3]取出來的結果作為命令使用,當然num=3是執行成功了的哈。
shell的條件判斷
test判斷
-e 測試檔案或目錄是否已存在,存在則返回真,否則返回假
-f 判斷檔案是否是普通檔案型別
-d 判斷是否是目錄型別
-n 判斷字串不為空則為真,理解為 no zero
-z 判斷字串為空時候為真,理解為 zero
-r 判斷當前使用者對該檔案是否有許可權檢視
-u 判斷該檔案是否具有SUID屬性
-g 判斷該檔案是否具有SGID屬性
比較引數
-eq 相等
-ne 不相等
-gt 大於
-lt 小於
-ge 大於等於
-le 小於等於
-a (and) 比如:test -r hello -a -w hello
#判讀使用者對該hello檔案是否有r讀取和w寫入許可權
-o (or) 比如:test -r hello -o -w hello
#判斷使用者對該hello檔案是否有r讀取或者w寫入任意一個許可權
! (not) 表示非,例如:test ! -r hello
表示當使用者對該檔案不具有r讀取許可權的時候就返回true,
注意寫的時候! -r之間一定要有空格,否則會報錯,不能連在一起寫,因為-r是引數,除非你是等於號就可以連在一起!=
-
-e
簡單做一個練習:判斷hello檔案是否存在,存在則列印“檔案已存在” 否則列印 “檔案成功建立” 並且建立hello檔案
test -e "hello" && echo "檔案已存在" || (touch hello && echo "檔案成功建立")
-
-f
判斷這個檔案是否是普通檔案型別,如果是就列印yes否則列印no
test -f hello && echo yes || echo no
-
-d
判斷是否是目錄型別
test -d hello && echo yes || echo no
-
-n
判斷字元是否為空,不為空則列印str_yes,否則列印str_no
str='' test -n "$str" && echo str_yes || echo str_no
-
-r
判斷使用者對檔案是否有r讀取許可權
test -r "hello" && echo yes || echo no
-
-w
判斷使用者對檔案是否有w寫許可權
-
-x
判斷使用者對檔案是否有x執行許可權
注意:如果你使用root來測試的話只能返回yes,因為root許可權是最大的。
中括號[]判斷
注意:[]的兩邊一定要一定要加上空格,否則會出錯,這裡在if使用中括號的時候是一樣的。
檔名和目錄名最好使用引號引起來,雖然可以不用引號,但是不敢擔保你的檔案是否有空格隔開。
格式:[ 條件表示式 ]
引數
-f 測試普通檔案是否存在
-d 測試目錄是否存在
-r
-w
-x
以上三個都是判斷使用者對檔案是否有r讀取、-w寫入、-x執行許可權。
比較引數
-eq 相等
-ne 不相等
-gt 大於
-lt 小於
-ge 大於等於
-le 小於等於
-a (and) 比如:[ -f hello -o -f world ] && echo yes || echo no
-o (or) 比如:[ -f hello -o -f world ] && echo yes || echo no
! (not) 表示非,例如:[ ! -f hello ] && echo yes || echo no
表示當使用者對該檔案不具有r讀取許可權的時候就返回true,
注意寫的時候! -r之間一定要有空格,否則會報錯,不能連在一起寫,因為-r是引數,除非你是等於號就可以連在一起!=
-
判斷hello檔案是否存在,存在就輸出yes 否則就輸出 no
[ -f hello ] && echo yes || echo no
-
判斷hello目錄是否存在,存在就輸出yes 否則就輸出no
[ -d hello ] && echo yes || echo no
中括號寫判斷符號
注意:在中括號中寫判斷符號的時候,資料與判斷符號和資料之間都要用空格隔開,包括中括號也要空格哈,比如下面
(後面會解釋為什麼使用轉移符號=)
[ "${name}" \= "123" ] && echo yes || echo no
注意:這裡使用一個等號還是兩個等號都是一樣的哈。
意思是如果該變數name等於123的話就echo yes 否則 no
同理其他單個的符號也要空格,雙個不用
[ 1 \= 1 ]
使用數學比較符號的時候記得使用轉義符
[ 1 \> 2 ] 這樣才對,否則你使用[ 1 > 2 ]是錯誤的
下面兩語句執行以下就知道結果了
[ 1 \> 2 ] && echo yes || echo no
[ 1 > 2 ] && echo yes || echo no
總結:使用數學運算子的話,如果是單個字元的就要使用轉移符號\,如果是!=或者==或者>=這種就不用加轉義符號。
雙中括號[[]]
格式:[[ 條件表示式 ]]
使用起來和單中括號的區別就是:雙中括號不用寫轉義符,就可以直接識別> < = ,那麼雙中括號同時還支援正規表示式匹配。
然後其他單中括號支援的在雙中括號裡也能用。
但是我們平時用的比較多的是單中括號。
總結括號的知識:
if分支
注意:條件表示式可以是很多種,我們上面學的shell條件判斷方式否可以使用
單分支
if <條件表示式>
then
codeing...
fi
#簡化
if <條件表示式>;then
codeing
fi
巢狀if
if <條件表示式>
then
codeing...
if <條件表示式>
then
fi
fi
if-else
if <條件表示式>
then
coding...
else
coding...
fi
if-elif
if <條件表示式>
then
coding
elif <條件表示式>
then
coding
else
coding...
fi
case
注意:case的出現是可以讓你少用if else,同時他也是一個選單化的這麼一個命令。
格式
語法
case $choice in
1)
shell程式碼
;;
2)
shell程式碼
;;
3)
shell程式碼
;;
*) #這個是固定的,相當於我們程式設計中的default,就是上面的選項都沒有被$變數的值的時候就會跳到這裡來,在shell中一般是用來寫提示資訊的。
shell程式碼
;;
esac
#為什麼是esac,很簡單,我們學習if的時候是用fi解釋,就是if倒過來,那麼case的結束也是倒過來esac
注意:
1)
2)
3)
裡面的123可以隨便,只要你的$變數可以找到這個裡面,你可以隨便寫,可以是:
qqq)
reg)
adgsaf)
*)
隨便寫,只要你的$變數能夠找到即可。
*)最後一個是固定的
for
語法
for 變數名 in 迴圈列表(一定是列表哈)
do
shell程式碼
done
注意
迴圈列表可以是:
{1..100} shell命令自帶的生成1-100的序列,當然其他命令都可以比如seq
"1 2 3 4" 這種也能夠當成迴圈列表,以空格為分隔符號,會依次取出1 2 3 4
文字檔案中你cat出來每一行也能當作列表迴圈
for i in $(cat /etc/passwd) #這種讀取檔案出來就是每一行作為一個變數
$()等於``,執行命令,不要忘記了,在這裡回顧一下。
while
語法
while <條件表示式>
do
shell程式碼
done
注意
這個條件表示式和之前學的一樣,shell的條件判斷表示式能寫的while照樣可以拿來當條件表示式,不過一般都是用 [] 單中括號的比較多
普通陣列
定義
陣列名=(值1 值2 值3)
取陣列值
#根據下標取值
${陣列名[下標]} #不能用$陣列名[下標] ,取不到陣列值
#取陣列中所有的值
${陣列名[*]}
${陣列名[@]}
#計算陣列長度
${#陣列名[@]}
${#陣列名[*]}
#解釋:這裡很巧妙使用了我們上面說的#取字元長度的寫法,這裡就變成了取陣列長度。比如$*和$@都是取所有引數,然後我們這裡就是取陣列所有值,那麼再結合之前學的統計字元長度${#name}這種,就完美解釋和理解了為什麼計算陣列長度是用${#陣列名[@]}這個樣子了。
函式
說明:函式必須先定義再執行
注意:return 和 exit 是不同的,exit是結束shell環境,而return是結束函式執行。
函式定義
語法格式如下
#第一種
function 函式名(){
函式體
return 返回值
}
#第二種
function 函式名{
函式體
return 返回值
}
#第三種
函式名(){
函式體
return 返回值
}
函式執行
#執行方式
函式名
函式名(引數1 [,引數2,...] )
函式傳參
說明:在shell指令碼中,傳參方式和你輸入的引數一樣,我們平常的比如touch ,touch 傳引數是 touch 檔名,那麼函式傳參也是這樣。同時,函式獲取引數方式和指令碼獲取引數方式是一樣的。(這句話理解不了就看下面的例子)
function 函式名(){
echo $1 $2 $3
return 返回值
}
#函式有引數的呼叫方式如下(和指令碼傳參一樣格式)
函式名 引數1 引數2 引數3
示例指令碼:
[root@localhost cxk]# cat args_sh.sh
function f(){
echo $1 $2 $3
echo "函式執行完畢。"
return 0
}
f "var1" "var2" "var3"
exit 0
[root@localhost cxk]# bash args_sh.sh
var1 var2 var3
函式執行完畢。
如何分檔案
如果你要分檔案,那麼可以使用source 或者 .符號進行當前執行shell的bash中載入進來,所以這就是為什麼我們不同shell之間就算定義了系統變數,也要重新載入的原因了吧 。
fun.sh檔案
. fun.sh
fun1
fun2
exit 0
main.sh檔案
#!/bin/bash
fun1(){
echo "fun11111 runing..."
}
fun2(){
echo "fun22222 runing..."
}
執行main.sh檔案(可以看到成功分檔案執行成功)
[root@localhost cxk]# bash main.sh
fun11111 runing...
fun22222 runing...
[root@localhost cxk]#
規範
學過程式設計的同學都知道,我們程式主入口的名字一般都是main,
雖然我們shell中沒有固定的main函式,但是我們還是進行一些規範化操作。
比如:
fun1(){}
fun2(){}
在定義了一系列函式之後,我們最後要寫一個main函式來規範化我們的程式碼執行邏輯,一方面是方便閱讀,二來也是對指令碼的封裝模組化能夠有更深的理解。