一、基礎概念
編程語言有低級語言和高級語言兩類。
高級語言也有編譯型和解釋型的區分,編譯型語言有C,C++,jave;解釋型語言有shell,perl,python
shell腳本是一種包含聲明或命令,并符合一定格式的文本文件
shell作為一種過程式解釋型的編程語言,它的基本組織結構有:
數據存儲:變量、數組
表達式
語句:if while case
shell腳本的格式有:
#!SHEBANG
CONFIGURATION_VARIABLES
FUNCTION_DEFINITIONS
MAIN_CODE
shell腳本格式要求首行必須是shebang機制 ,shebang也是指明文件調用的解釋器類型
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
- 創建shell腳本
第一步:使用文本編輯器來創建文本文件
第一行必須包括shell 聲明序列:#!
#!/bin/bash
添加注釋
注釋以# 開頭
第二步運行腳本:
給予執行權限,通過具體的文件路徑指明文件執行。
直接運行解釋器,將腳本作為解釋器程序的參數運行。
- 腳本調試
檢測腳本中的語法錯誤
bash -n /path/to/some_script
調試分布執行
bash -x /path/to/some_script
-
執行腳本
/root/danran.sh 絕對路徑,可以作用于子進程 . /danran.sh 相對路徑,可以作用于子進程 bash /root/danran.sh 可以作用于子進 source /root/danran.sh 不會打開子進程,僅僅作用于當前進程
二、變量: 命名的存儲空間
數據的存儲方式有:
字符:
數值:整型,浮點型
變量類型:
作用:
1、定義數據存儲格式
2、定義參與的運算
3、表示的數據范圍
類型:
字符
數值:整型,浮點數
編程語言的變量又有強類型和弱類型的區分
強類型:jave,C++
變量不經過強制轉換,它永遠是這個數據類型,不允許隱式的類型轉換,故需要定義數據類型
弱類型:bash
變量會有隱式轉換,故不需要指定數據類型,所有要存儲的數據都當做字符進行,不支持浮點數
邏輯運算
與:
1 && 1 = 1
1 && 0 = 0
0 && 1 = 0
0 && 0 = 0
或:
1 || 1 = 1
1 || 0 = 1
0 || 1 = 1
0 || 0 = 0
非:
!1 = 0
!0 = 1
異或:相同為假,不同為真
短路運算:
短路與:
第一個為0,結果必為0
第一個為1,第二個必須要參與運算
短路或:
第一個為1,結果必為1
第一個為0,第二個必須參與運算

三、bash中的變量的種類
根據變量的生效范圍為標準:
本地變量:生效范圍為當前shell進程,對當前shell之外的其他的shell進程無效,包括當前shell的子shell進程均無效;
環境變量:生效范圍為當前shell進程及其子進程;
局部變量:生效范圍為當前shell進程中某代碼片段(通常指函數);
位置變量:$1,$2,...來表示,用于讓腳本在腳本代碼中調用通過命令行傳遞給它的參數;
特殊變量:$?,$0,$*,$@,$#,$$
本地變量:
變量賦值:name=“value”
可以使用單引號:
value:
(1)可以是直接賦值:name=“filename”
(2)變量引用:name=“$username”
(3)命令引用:name=`COMMAND`,name=$(COMMAND)
變量引用:$(name),$name
"":弱引用,其中的變量引用會被替換為變量值;
'':強引用,其中的變量引用不會被替換為變量值,而保持原字符串;
顯示已定義的所有變量:
set
撤銷變量:
unset name
環境變量:
變量聲明、賦值
export name=VALUE
declare -x name=VALUE
變量引用:$name,$(name)
顯示所有環境變量:
export
env
printenv
declear -x
撤銷變量:
unset name
bash中有很多內置的環境變量,PATH,SHELL,UID,HISTSIZE,PWD,OLD,HISTFILE,PS1,_
_:上一個命令的最后一個字符串

-
變量命名法則:
1、不能使用程序中的保留字,如if、for
2、只能使用數字、字母及下劃線,且不能以數字開頭
3、最好見名知義
只讀變量:
設置只讀變量:
readonly name
declare -r name
查看只讀變量:
readonly -p
位置變量:
在腳本代碼中調用通過命令行傳遞給腳本的參數:
$1,$2,....:對應調用第1,第2....個參數,$10以上的應使用括號${10} ,${12}
shift [n] 調換位置,n不能大于參數的個數
$0:調用命名本身
$*:傳遞給腳本的所有參數,將所有參數作為一個整體字符傳遞
$@:傳遞給腳本的所有參數,每一個參數獨立傳遞,與$*只有在使用“”引起來時才有區別
$#:傳遞給腳本的參數個數
set --:清空所有位置變量參數
特殊變量:$?,$0,$*,$@,$#,$$
$?:返回狀態碼
$$:返回當前進程的進程編號
image![]()
![]()
四、bash的配置文件
按生效范圍劃分:存在兩類
全局配置:
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
個人配置:
~/.bash_profile
~/.bashrc
按功能劃分:存在兩類
profile類:為交互式登錄的shell提供配置
全局:/etc/profile,/etc/profile.d/*.sh
個人:~/.bash_profile
功用:
(1)用于定義環境變量
(2)運行命令和腳本
bashrc類:為非交互式登錄的shell提供配置
全局:/etc/bashrc
個人:~/.bashrc
功用:
(1)定義命令別名
(2)定義本地變量
修改文件后生效:
1、重啟shell進程
2、source ~/.bashrc
. ~/.bashrc
bash退出任務
保存在~/.bash_logout 文件中(用戶)
在退出登錄shell 時運行
用于
創建自動備份
清除臨時文件
shell登錄:
交互式登錄:
直接通過終端輸入賬號密碼;
使用“su -UserName”或“su -l UserName”切換的用戶
讀取文件的順序:
/etc/profile --> /etc/profile.d/*.sh --> ~/.bash_profile --> ~/.bashrc --> /etc/bashrc
非交互式登錄:
su UserName
圖形界面下打開終端
執行腳本
文件讀取順序:
~/.bashrc --> /etc/bashrc --> /etc/profile.d/*.sh
編輯配置文件定義的新配置的生效方式:
(1)重新啟動shell進程
(2)使用source或.命令進程
五、bash中進行算數運算
+,-,*,/,%,...
實現算數運算:
(1)let var=算數表達式
sum1=3;sum2=4;let sum=$sum1+sum2
(2)var=$(算術表達式)
(3)var=$((算術表達式))
(4)var=$(expr arg1 arg2 arg3...) expr命令,參數之間用空格隔開
sum=$(expr $num1 \* $num2)
*在某些場景下需要轉義
(5)declare -i var = 數值
(6)echo '算術表達式' | bc
bash有內置的隨機數生成器:$RANDOM
echo $[$RANDOM%60] 對60取模,即生成0-59之間的某個數
echo $[$RANDOM%60+1] 取1-60之間的某數
增強型賦值:
+=,-=,*=,/=,%=
count+=1 <==> count=count+1
自增,自減:
let var+=1 <==> let var++
let var-=1 <==> let var--
練習:
1、編寫腳本sumid.sh,計算/etc/passwd文件中的第10個用戶和第20個用戶的id之和
#! /bin/bash
user10="`head -n $2 $1 | tail -n 1|cut -d: -f3`"
user20="`head -n $3 $1 | tail -n 1|cut -d: -f3`"
let sum=$user10+$user20
echo "user id sum is $sum"

2、編寫腳本/sumspace.sh,傳遞兩個文件路徑作為參數給腳本,計算這兩個文件中所有空白行之和
#!/bin/bash
spaceline1=`grep "^[[:space:]]*$" $1 | wc -l`
spaceline2=`grep "^[[:space:]]*$" $2 | wc -l`
echo "The sum of space line:$[spaceline1+spaceline2]"

3、生成腳本模板
#!/bin/bash
[ $# -gt 1 ] &&{ echo "the args is error";exit;}
[ $# == 0 ] && read -s -p "please input script name: " name || name="$1"
echo "#!/bin/bash
# filename "$name"
# author:danran
# time is `date +%F`" >"$name"
chmod +x "$name"
vim "$name"
4、編寫腳本,統計/etc、/usr、/var目錄中有多少個一級子目錄和文件
#!/bin/bash
# danran
# time is Mon Jun 5 13:09:12 CST 2017
line1=`ls $1 | wc -l`
line2=`ls $2 | wc -l`
line3=`ls $3 | wc -l`
let sum=$line1+$line2+$line3
echo $sum
六、條件測試
判斷某需求是否滿足,需要由測試機制來完成;
Nnote:專用的測試表達式需要由測試命令輔助完成測試過程;
評估布爾聲明,以便用在條件性執行中
若真,則返回0
若假,則返回1
測試命令:
[:內部命令
[[:shell關鍵字
測試命令:
test EXPRESSION
[ EXPRESSION ]
[[ EXPRESSION ]]
Note:EXPRESSION前后必須有空白字符
bash的測試類型:
數值測試
-gt:大于
-ge:大于等于
-eq:等于
-ne:不等于
-lt:小于
-le:小于等于
字符串測試
==/=:等于
>:大于
<:小于
!=:不等于
~=:左側字符是否能被右側的PATTERN所匹配
Note:一般用于[[ ]]中
-z “STRING” 測試字符串是否為空,空為真,不空為假
-n “STRING” 測試字符串是否不空,不空為真,空為假
eg [ -z "$name" ]
[[ $name ~= ^a ]] 匹配$name是否a開頭
-v VAR 檢查變量VAR是否設置
eg:[ -v var ]
Note:用于字符串比較時的用到的操作數都應使用引號
文件測試
存在性測試:
-a:FILE 同-e
-e:FILE:文件存在性測試,存在為真
存在性及類別測試:
-b FILE:是否存在且為塊設備文件
-c FILE:是否存在且為字符設備文件
-d FILE:是否存在且為目錄文件
-f FILE:是否存在且為普通文件
-S FILE:存在且為套接字文件
-L FILE 或 -h FILE:存在且為鏈接文件
-p FILE:是否存在且為命名管道文件
先判斷文件是否為鏈接文件,在判斷文件的類型,因為測試鏈接文件時是檢測鏈接原文件,故應先判斷是否為鏈接文件
![]()
文件權限測試:(當前用戶)判斷文件權限時,首先判斷文件是否存在
-r:文件是否存在且可讀
-w:文件是否存在且可寫
-x:文件是否存在且可執行
文件特殊權限測試:
-g FILE:文件存在且擁有guid權限,suid對腳本無效,腳本不是二進制文件
-u FILE:文件存在且擁有suid權限
-k FILE:是否存在且擁有sticky權限
文件大小測試:
-s FILE:文件是否存在且非空
文件打開性測試:
-t fd:fd表示文件描述符是否已經打開且與某終端相關
-N FILE:文件自從上一次打開讀取之后是否被修改過
-O FILE:當前有效用戶是否為文件屬主
-G FILE:當前有效用戶是否為文件屬組
雙目測試:
FILE1 -ef FILE2:FILE1與FILE2是否指向同一個設備上的相同inode,即硬鏈接
FILE1 -nt FILE2:FILE1 是否新于FILE2
FILE1 -ot FILE2:FILE1 是否舊于FILE2
組合條件測試:
邏輯運算:
第一種方式:
COMMAND1 && COMMAND2 短路與
COMMAND1 || COMMAND2 短路或
! COMMAND 非
第二種方式:
[ EXPRESSION1 -a EXPRESSION2 ] 與
[ EXPRESSION1 -o EXPRESSION2 ] 或
!EXPRESSION 非
必須使用測試命令進行
[ -f /bin/cat -a -x /bin/cat ] && echo "true"
[ ! -r /app/a -a ! -w /app/a ] && echo true <==> [ ! \( -r /app/a -o -w /app/a \) ] && echo "true"
練習:
1、編寫腳本/root/bin/excute.sh,判斷參數文件是否為sh后綴的普通文件,如果是,添加所有人可執行權限否則提示用戶非腳本文件
#!/bin/bash
[ $# == 0 ] && read -p "please input fimename" name || name=$1
[ -f $name ] && [[ "$name" =~ \.sh$ ]] && chmod a+x $name || echo "$name not scripts file"
2、編寫腳本/bin/per.sh,判斷當前用戶對指定的參數文件,是否不可讀并且不可寫
#!/bin/bash
[ ! -r $1 -a ! -w $1 ] && echo "$1 file not read and not write"
3、編寫腳本/root/bin/checkdisk.sh,檢查磁盤分區空間和inode使用率,如果超過80%,就發廣播警告空間將滿
#!/bin/bash
inode=`df -i | grep "^/dev/sd*" | tr -s ' ' '%'| cut -d'%' -f5| sort -r | head -n 1`
disk=`df | grep "^/dev/sd*" | tr -s ' ' '%'| cut -d'%' -f5| sort -r | head -n 1`
[ $inode -gt 80 ] && echo "danran"
[ $disk -gt 80 ] && echo "dan"
七、bash自定義退出狀態碼
exit [n]:自定義退出狀態碼為n
Note:腳本中一旦遇到exit命令,腳本會立即終止,終止退出狀態取決于exit命令后面的數字n。
Note:如果未給腳本指定退出狀態碼,整個腳本的退出狀態碼取決于腳本中執行最后一條命令的狀態碼。
進程使用退出狀態來報告成功或失敗
0 代表成功,1 -255 代表失敗
$? 變量保存最近的命令退出狀態
練習:編寫腳本/root/bin/argsnum.sh,接受一個文件路徑作為參數;
如果參數個數小于1,則提示用戶“至少應該給一個參數”,并立即退出;
如果參數個數不小于1,則顯示第一個參數所指向的文件中的空白行數
#!/bin/bash
[ $# -lt 1 ] && (echo "please input a arge" && exit ) || echo "`grep "^$" $1 | wc -l`"

八、read
使用read 來把輸入值分配給一個或多個shell 變量
-p 指定要顯示的提示
eg:read -p "danran" file
-s 靜默輸入,一般用于密碼
-n N 指定輸入的字符長度N
-d ‘ 字符’ 輸入結束符
-t N TIMEOUT 為N秒,當等待時機超過N秒時自動退出
read 從標準輸入中讀取值,給每個單詞分配一個變量,所有剩余單詞都被分配給最后一個變量,多個字符使用重定向賦值多個變量時,使用>>>三個重定向方可
![]()
![]()
![]()
- bash如何展開命令行
把命令行分成單個命令詞
展開別名
展開大括號的聲明({})
展開波浪符聲明(~)
命令替換$() 和 ``)
再次把命令行分成命令詞
展開文件通配(* 、? 、[abc] 等等)
準備I/0 重導向(< 、>)
運行命令
防止擴展:
反斜線(\ )會使隨后的字符按原意解釋
加引號來防止擴展
? 單引號(’ )防止所有擴展
? 雙引號(” )也防止所有擴展,但是以下情況例外:
$ (美元符號) - 變量擴展
` (反引號) - 命令替換
\ (反斜線) - 禁止單個字符擴展
! (嘆號) - 歷史命令替換