本篇文章就是一個過渡學習的,先入門shell腳本,由於作者有編程基礎,所以有些解釋的比較少。由於現在還在努力學習中,以後等本散修進階了之後再寫進階的、與網路安全更加貼合的shell編程 ...
目錄
前言
本篇文章就是一個過渡學習的,先入門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函數來規範化我們的代碼執行邏輯,一方面是方便閱讀,二來也是對腳本的封裝模塊化能夠有更深的理解。
本文來自博客園,作者:竹等寒,轉載請註明原文鏈接。