Linux-shell程式設計入門基礎

竹等寒發表於2024-07-10

目錄
  • 前言
  • 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
然後再載入你下面定義的變數等等

image
這裡提前說明一下如何載入,注意是載入不是執行

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中
    image
  • 下面是透過sh進入的一個子shell,透過pstree可以看到確實是進入了一個子shell中
    image

檢視一下兩個shell,pstree可以看到是不同bash的。
image
我們隨便定義一個變數,可以看到不同shell之間的變數是互通不了的,原因作用域的問題

作用域大概如下,age一般定義在自己bash中的變數是不能互通的:

每一個盒子都是一個shell盒子,他們之間的變數都是不能互通的
不管是他們的全域性變數還是區域性變數都是不可以互通
但是除非你定義在了系統變數,比如/etc/profile這種檔案中,
這種檔案是在系統載入的時候載入的檔案,所以每一個使用者都會生效。
但是除非你自己盒子裡面有和你在系統檔案中定義的變數,就會被覆蓋掉,以你自身的.bash_profile檔案為準
image
總結:

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或者.符號進行載入才可以載入。
image
可以看到我們載入後就能夠看到和使用另一個shell新增進去的name變數了。
image
透過上面案例後,我們可以理解成:

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

image

$特殊狀態變數

$? 上一次命令執行狀態返回值,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

image

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的理解

下面是我看超哥教程的一份圖解:
image

為什麼要理解父子shell的關係,原因是因為我們shell程式設計中一個括號就能夠開啟一個子shell,目的是不讓某些操作比如Ping指令將我們當前的shell卡主,這樣的話你可以開一個子shell去執行ping這種指令,然後可以讓shell指令碼繼續執行下去。

image
$BASH_SUBSHELL該變數是檢測開啟了幾個子shell

(cd ~;pwd;ls;(cd /tmp;ls;(echo "開啟了:$BASH_SUBSHELL 個子shell")))

image

內建和外接命令

識別內建還是外接

type cd
type rename

快速檢視系統有哪些是內建命令

compgen -b

區別


先認識一下兩個的意思

  • 內建

    在系統啟動時就載入記憶體,常駐記憶體,執行效率更高,但是佔用資源
    
  • 外接

    系統需要從硬碟中讀取程式檔案,再讀入記憶體載入
    

二者區別:

內建就是不開啟子程序,直接在當前shell中執行,外接就是需要下載的一些系統命令,使用外接命令的時候需要開啟一個子程序執行。

數值計算

雙括號(())運算

image

((i=i+1))  #雙括號裡面進行一系列的運算
$((i=i+1)) #加個$符號就是取你計算出來的結果
((條件判斷)) #為真返回1,為假返回0

注意:

假設你有一個變數名為name=123
當你使用雙括號進行name變數運算的時候是按照你name本來的值進行運算的,同時可以修改name的值。
((name++))

echo $name
124

計算器簡易版

#!/bin/bash
echo $(($1))

image

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 ]

image
使用數學比較符號的時候記得使用轉義符

[ 1 \> 2 ] 這樣才對,否則你使用[ 1 > 2 ]是錯誤的

下面兩語句執行以下就知道結果了
[ 1 \> 2 ] && echo yes || echo no
[ 1 > 2 ] && echo yes || echo no

總結:使用數學運算子的話,如果是單個字元的就要使用轉移符號\,如果是!=或者==或者>=這種就不用加轉義符號。

雙中括號[[]]

格式:[[ 條件表示式 ]]

使用起來和單中括號的區別就是:雙中括號不用寫轉義符,就可以直接識別> < = ,那麼雙中括號同時還支援正規表示式匹配。

然後其他單中括號支援的在雙中括號裡也能用。
但是我們平時用的比較多的是單中括號。

總結括號的知識:

image

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函式來規範化我們的程式碼執行邏輯,一方面是方便閱讀,二來也是對指令碼的封裝模組化能夠有更深的理解。

相關文章