shell指令碼程式設計基礎
【1】、shell概述
-
shell是一門程式語言,如:C、C++、java、PHP、python Go等
-
語言分類
程式設計型語言:C、C++、Go等為編譯型語言。程式執行需要提前編譯,編譯語言都有編譯器
解釋型語言:shell、PHP、python等為解釋型語言,程式在執行時不需要提前編譯,一邊執行,一邊解釋,每種解釋型語言都有直譯器
-
shell語言支援大部分程式語言都具備的功能:if判斷、for迴圈、函式、陣列、加減乘除、邏輯運算等
-
指令碼的本質就是命令的堆積
【2】、shell編寫規範
系統直譯器
[root@xu script]# cat /etc/shells
/bin/sh #Unix直譯器
/bin/bash #Linux預設直譯器
/usr/bin/sh
/usr/bin/bash
這就是一個簡單的shell script,在我們編寫完成後,要給script加上可執行的許可權
#!/bin/bash
#首先要進行環境指定,指定編譯器,一般我們會使用/bin/bash,標準直譯器
# '#'表示註釋、‘#!’表示指定直譯器
#功能:輸出hello world
echo hello world
在指令碼中不能出現互動式命令
#例如建立使用者並且給使用者設定密碼
#!/bin/bash
#creat user
useradd zhanghaowei
echo 123 | passwd --stdin zhanghaowei
#我們使用設定密碼的方式採用非互動式的方式
【3】、如何寫好一個shell指令碼
- 明確任務需求
- 按照需求整理好每一個步驟,先後順序
- 執行指令碼,並根據執行結果排除錯誤
- 最佳化指令碼並達到最後的結果
- 指令碼程式規範
-
宣告直譯器(shebang)
-
!/bin/bash
-
-
註釋
- 對指令碼的描述
-
執行指令
- Linux命令
-
【4】、編寫指令碼
-
自動配置本地yum倉庫的指令碼
[BaseOS] name=local_BaseOS baseurl=file:///mnt/cdrom/BaseOS enable=1 gpgcheck=0 [AppStream] name=local_AppStream baseurl=file:///mnt/cdrom/AppStream enable=1 gpgcheck=0 EOF sleep 2 echo "======永久掛載光碟機======" echo "/dev/cdrom /mnt/cdrom iso9660 defaults 0 0" >> /etc/fstab sleep 2 echo "======檢查軟體包數量======" yum list | wc -l
【5】、指令碼的執行方式
-
方式一:賦予指令碼執行許可權後,可用絕對路徑和相對路徑執行
-
方式二:呼叫直譯器執行指令碼檔案,會開啟jie'shi'q
-
方式三:source和 . :執行指令碼檔案
-
#絕對路徑執行指令碼 [root@xu script]# /script/hello.sh hello world #相對路徑執行指令碼 [root@xu script]# ./hello.sh hello world
-
#呼叫直譯器執行指令碼,呼叫直譯器時可以不給執行許可權 [root@xu script]# bash /script/hello.sh hello world
-
父子shell問題
#我們知道指令碼的執行是透過呼叫bash直譯器去執行,我們透過sshd連線一個會話可以存在多個bash程序 ─sshd(1065)───sshd(1896)───sshd(1900)───bash(1901)───bash(2093)───bash(2114)───pstree(2135) #透過pstree我們可以看到當前session存在多個bash環境,都是由systemd程序建立出來的,而我們現在處於bash(2114)環境中,如果此時我們執行指令碼採用的環境時bash(2114)環境。 #如果我們使用路徑(source)去執行指令碼和使用bash去執行指令碼就會產生區別 #採用.(source)去執行指令碼,是在當前的bash環境下執行的,而使用bash或path去執行指令碼則是在當前的bash環境下建立出一個子bash去執行指令碼,並切換當前環境為新的指令碼
【6】、變數
-
shell中變數型別只有string,沒有別的;比方說在python中變數是有不同的型別的(list、dic、str。。。),但是在shell中只有str.
-
變數的存在讓指令碼變得更加靈活,能夠應對不同的場景
-
語法:變數名="變數值"
-
$:呼叫變數
[root@xu ~]# name=$(hostname) [root@xu ~]# echo $name xu
1、變數分類
(1)、系統定義變數
# 系統自定義的系統變數
[root@moudle01 17:23:53 /script]# echo $PWD
/script
[root@moudle01 17:23:59 /script]# echo $SHELL
/bin/bash
[root@moudle01 17:24:05 /script]# echo $HOME
/root
[root@moudle01 17:24:14 /script]# echo $USER
root
[root@moudle01 17:24:51 /script]# echo $UID
0
(2)、位置變數和預定義變數
在shell中也存在著和C語言相似的引數傳遞問題
只不過在shell中的引數問題是透過命令去完成的,我們在執行一個shell scrip時可以寫上引數,將引數傳遞進shell script,同時在shell中去接收透過命令傳入的引數。
位置引數:在執行指令碼的命令中寫上的引數就是位置引數,從1開始
在接收時可以使用以下識別符號
- $0:提取檔名(用source執行指令碼時,不顯示檔名而是直譯器名字)
- ${1}:提取位置引數為1的值
- ...
- ${20}: 提取位置引數為20的值
- $*:接收所有引數
- $@:接收所有引數
- $$:提取當前程序的PId
- $?:上一條命令的執行結果(上一個程式/命令的返回狀態碼)
- $#:所有引數個數
#!/bin/bash
echo $1
echo $2
echo $3
echo $0
echo $*
echo $#
echo $$
echo $?
[root@moudle01 21:10:40 /script]# bash test04.sh a b c d
a
b
c
test04.sh
a b c d
4
2728
0
[root@moudle01 21:18:33 /script]# kjasdhfsdlgasgasgadfg
-bash: kjasdhfsdlgasgasgadfg: command not found
[root@moudle01 21:18:45 /script]# echo $?
127
[root@moudle01 21:18:49 /script]# echo $?
0
建立使用者tom,密碼123
#!/bin/bash
useradd $1
echo $2 | passwd --stdin $1
2、env和set
env:所有環境變數
set:所有變數
[root@moudle01 21:30:10 /script]# abcd=6666666
[root@moudle01 21:30:55 /script]# set | grep abcd
abcd=6666666
[root@moudle01 21:31:00 /script]# env | grep PATH
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
3、常用的特殊符號
-
“”:雙引號,引用整體,界定範圍
-
‘’:單引號,引用整體,並且取消所有特殊字元含義,界定範圍
[root@moudle01 21:32:13 /script]# a=" a" [root@moudle01 21:36:17 /script]# echo "$a" a # 如果有特殊符號,定義時單引號和雙引號都行,在引用時只能使用雙引號 [root@moudle01 21:36:25 /script]# echo '$a' $a
-
$[]、$(())、expr:四則運算
[root@xu script]# echo $[1+1] 2 [root@xu script]# echo $[2*3] 6 [root@xu script]# echo $[2-3] -1 [root@xu script]# echo $[6/3] 2 [root@xu script]# echo $[10%3] 1 # expr 運算子號兩側必須加空格 [root@moudle01 16:13:56 ~]# expr 1 + 1 2
-
$():取命令結果作為引數
-
``:反撇號,將命令的輸出結果作為引數
[root@moudle01 21:40:57 /script]# date Wed Jun 12 21:41:07 CST 2024 [root@moudle01 21:38:26 /script]# a=`date` [root@moudle01 21:40:54 /script]# echo $a Wed Jun 12 21:40:54 CST 2024 [root@moudle01 21:41:07 /script]# x=$(ls) [root@moudle01 21:42:37 /script]# echo $x abc rsync_inotify.sh test01.sh test02.sh test03.sh test04.sh test05.sh
4、read
互動式輸入變數
[root@moudle01 21:48:04 /script]# read -p "請輸入:" a
請輸入:123
[root@moudle01 21:48:47 /script]# echo $a
123
#!/bin/bash
read -p "請輸入要建立的使用者名稱:" a
useradd $a
read -p "請輸入使用者的密碼:" p
echo $p | passwd --stdin $a
[root@moudle01 21:50:41 /script]# source test05.sh
請輸入要建立的使用者名稱:xxx
請輸入使用者的密碼:123
Changing password for user xxx.
passwd: all authentication tokens updated successfully
5、遮蔽回顯
在敲完之後,你在後面輸入的所有內容都看不到
[root@moudle01 21:51:16 /script]# stty -echo
顯示回顯
[root@moudle01 21:51:16 /script]# stty echo
寫入建立使用者的指令碼,讓密碼輸入更加安全
#!/bin/bash
read -p "請輸入要建立的使用者名稱:" a
useradd $a
stty -echo
read -p "請輸入使用者的密碼:" p
stty echo
echo ""
echo $p | passwd --stdin $a
[root@moudle01 21:59:24 /script]# source test05.sh
請輸入要建立的使用者名稱:aaa
請輸入使用者的密碼:
Changing password for user aaa.
passwd: all authentication tokens updated successfully.
6、export命令
-
環境變數(全域性變數):系統在全域性生效的變數
普通變數(區域性變數):只針對區域性環境生效,如當前某一個login.bash指令碼生效
全域性變數和區域性變數就是相對於父子shell(bash)。環境變數(全集變數)就是父shell也可以使用,普通變數(區域性變數)只有子shell可以使用
-
變數生存週期
1、永久變數:寫入檔案,反覆讀取、載入,讓其永久生效,如/etc/profile的PATH修改
2、臨時變數:如命令列export定義的一個變數儲存資料,關閉shell後失效
-
在定義變數時有兩種選擇
-
1、加export:在父shell中定義的變數可以在子shell中去使用,也就是全域性變數
-
[root@xu ~]# export name="xuruizhao" [root@xu ~]# echo ${name} xuruizhao [root@xu ~]# bash Skill is acquired through repeated practice, and practice makes perfect. [root@xu ~]# bash Skill is acquired through repeated practice, and practice makes perfect. [root@xu ~]# echo ${name} xuruizhao
2、不加export:只對當前的shell有效,子shell看不到,也就是區域性變數
-
[root@xu ~]# name="hello world" [root@xu ~]# echo ${name} hello world [root@xu ~]# bash #我們切換了bash環境,也就是進入了子shell,由於沒有加export子shell中是不能使用父shell中定義的變數 Skill is acquired through repeated practice, and practice makes perfect. [root@xu ~]# echo ${name} [root@xu ~]#
-
7、let定義變數
使用let定義變數,可以在定義時進行計算
[root@moudle01 22:13:42 /script]# let a=1+1
[root@moudle01 22:14:21 /script]# echo $a
2
[root@moudle01 22:16:30 /script]# let a+=3
[root@moudle01 22:16:35 /script]# echo $a
5
8、bc命令
上面的運算均不支援小數
bc命令支援小數
[root@moudle01 16:13:58 ~]# echo "1.1+1" | bc
2.1
9、變數定義初值
#!/bin/bash
read -p "請輸入使用者名稱" username
if [ -z $username ];then
echo "使用者名稱不能為空" && exit
fi
useradd $username
read -p "請輸入密碼" pass
echo ${pass:-123456} | passwd --stdin $username
[root@moudle01 11:37:41 /script]# bash user_add02.sh
請輸入使用者名稱
使用者名稱不能為空
【7】、變數測試
兩種測試格式
test
[]
1、字串
(1)、陣列測試
== 判斷是否相同
! = 判斷兩側是否不等
[root@moudle01 16:29:46 ~]# a=1
[root@moudle01 16:29:51 ~]# b=2
[root@moudle01 16:29:52 ~]# [ $a != $b ]
[root@moudle01 16:30:04 ~]# echo $?
0
如果一個變數值為空,我們在進行比較時,
1、可以加上雙引號
2、-z:判斷變數是否為空
! -Z/-n:判斷變數是否非空
[root@moudle01 16:33:11 ~]# [ a == "$c" ]
[root@moudle01 16:33:23 ~]# echo $?
1
[root@moudle01 16:33:27 ~]# [ -z "$c" ]
[root@moudle01 16:35:13 ~]# echo $?
0
[root@moudle01 16:35:16 ~]# [ -z "$a" ]
[root@moudle01 16:35:22 ~]# echo $?
1
[root@moudle01 16:38:16 ~]# c=123
[root@moudle01 16:38:23 ~]# [ -n "$c" ]
[root@moudle01 16:38:26 ~]# echo $?
0
(2)、邏輯測試
&&與
||或
#!/bin/bash
[ $USER == root ] || echo "非管理員禁止執行該指令碼" && exit
yum -y install vsftpd > /dev/null
systemctl start vsftpd
systemctl enable vsftpd
# 只有root使用者可以執行此指令碼
#!/bin/bash
[ $USER != root ] && echo "非管理員禁止執行" && exit2
yum -y install vsftpd > /dev/null
systemctl start vsftpd
systemctl enable vsftpd
[root@moudle01 16:57:25 /script]# ls a || ls b && ls c || ls && ls /etc/yum.repos.d/
a
c
bak epel local_yum_house.repo
2、數字
-eq:相等
-ne:不相等
-gt:大於
-ge:小於等於
-lt:小於
-le:小於等於
#!/bin/bash
[ $# -ne 2 ] && echo "輸入變數數量錯誤" && exit
useradd $1
echo $2 | passwd --stdin $1
3、檔案
-e:判斷檔案是否存在,不關心檔案型別
-f:判斷檔案是否存在,必須是普通檔案
-d:判斷檔案是否存在,必須是目錄
[root@moudle01 17:19:49 /script]# [ -e a ]
[root@moudle01 17:20:00 /script]# echo $?
0
[root@moudle01 17:20:03 /script]# [ -d a ]
[root@moudle01 17:20:09 /script]# echo $?
1
[root@moudle01 17:20:10 /script]# [ -f x ]
[root@moudle01 17:20:21 /script]# echo $?
1
-r:判斷當前使用者對檔案是否有讀許可權(對root無效,只針對普通使用者)
-w:判斷當前使用者對檔案是否有寫許可權(對root無效,只針對普通使用者)
-x:判斷當前使用者對檔案是否有x許可權
[root@moudle01 17:21:45 /script]# [ -r a ]
[root@moudle01 17:22:02 /script]# echo $?
0
[root@moudle01 17:22:05 /script]# [ -w a ]
[root@moudle01 17:22:09 /script]# echo $?
0
[root@moudle01 17:22:10 /script]# [ -x a ]
[root@moudle01 17:22:17 /script]# echo $?
1
4、編寫指令碼
每隔兩分鐘檢查伺服器的賬戶數量,如果發現增加,則給管理員發郵件
#!/bin/bash
num=$(cat /etc/passwd | wc -l)
[ num -gt 38 ] && echo "使用者增加" | mail -s "user_info" root
# 編寫計劃任務
*/2 * * * * /script/test02.sh