1. 編程基礎(chǔ)
1.1 程序組成
程序: 算法+數(shù)據(jù)結(jié)構(gòu)
數(shù)據(jù): 是程序的核心
數(shù)據(jù)結(jié)構(gòu): 數(shù)據(jù)在計算機中的類型和組織方式
算法: 處理數(shù)據(jù)的方式
1.2 程序編程風格
面向過程語言:
- 做一件事情, 排出個步驟, 第一步干什么, 第二步干什么, 如果出現(xiàn)情況A, 做什么處理, 如果出現(xiàn)了情況B, 做什么處理
- 問題規(guī)模小, 可以步驟化, 按步就班處理
- 以指令為中心, 數(shù)據(jù)服務(wù)于指令
- C, Shell
面向?qū)ο笳Z言:
- 一種認識世界, 分析世界的方法論. 將萬事萬物抽象為各種對象
- 類是抽象的概念, 是萬事萬物的抽象, 是一類事物的共同特征的集合
- 對象是類的具象, 是一個實體
- 問題規(guī)模大, 復(fù)雜系統(tǒng)
- 以數(shù)據(jù)為中心, 指令服務(wù)于數(shù)據(jù)
- java, C#, Python, Golang等
1.3 編程語言
計算機:運行二進制指令
編程語言:人與計算機之間交互的語言。分為兩種:低級語言和高級語言
低級編程語言:
機器:二進制的0和1的序列诺祸,稱為機器指令。與自然語言差異太大开呐,難懂、難寫
匯編:用一些助記符號替代機器指令规求,稱為匯編語言
如:ADD A,B 將寄存器A的數(shù)與寄存器B的數(shù)相加得到的數(shù)放到寄存器A中
匯編語言寫好的程序需要匯編程序轉(zhuǎn)換成機器指令
匯編語言稍微好理解筐付,即機器指令對應(yīng)的助記符,助記符更接近自然語言
高級編程語言:
編譯:高級語言-->編譯器-->機器代碼文件-->執(zhí)行阻肿,如:C瓦戚,C++
解釋:高級語言-->執(zhí)行-->解釋器-->機器代碼,如:shell丛塌,python较解,php赴邻,JavaScript印衔,perl
編譯和解釋型語言:
編譯型語言不支持跨平臺, 因為編譯型語言的運行需要先把源代碼通過編譯器轉(zhuǎn)換成二進制文件, 最終運行的是二進制文件. 而不同平臺的編譯器將源代碼編譯后的二進制文件格式不同, 因此編譯型語言不支持跨平臺
解釋型語言如Python, 支持跨平臺, 因為解釋型語言是靠解釋器運行源代碼. 如果需要跨平臺, 那么只需要在不同平臺安裝對應(yīng)版本解釋器, 就可以直接運行源代碼
- Java
Java支持跨平臺, 將源代碼轉(zhuǎn)換成字節(jié)碼后, 把字節(jié)碼移植到不同的操作系統(tǒng), 通過不同操作系統(tǒng)上的Java虛擬機去轉(zhuǎn)換成二進制文件
1.4 編輯邏輯處理方式
順序結(jié)構(gòu)流程:
分支結(jié)構(gòu)流程:
循環(huán)結(jié)構(gòu)流程:
三種處理邏輯:
- 順序執(zhí)行:程序按從上到下順序執(zhí)行
- 選擇執(zhí)行:程序執(zhí)行過程中,根據(jù)條件的不同姥敛,選擇不同分支繼續(xù)執(zhí)行
- 循環(huán)執(zhí)行:程序執(zhí)行過程中需要重復(fù)執(zhí)行多次某段語句
2. Shell腳本語言的基本用法
2.1 Shell腳本的用途
自動化常用命令
執(zhí)行系統(tǒng)管理和故障排除
創(chuàng)建簡單的應(yīng)用程序
處理文本或文件
2.2 Shell腳本基本結(jié)構(gòu)
Shell腳本編程:是基于過程式奸焙、解釋執(zhí)行的語言
編程語言的基本結(jié)構(gòu):
- 各種系統(tǒng)命令的組合
- 數(shù)據(jù)存儲:變量彤敛、數(shù)組
- 表達式:a + b
- 控制語句:if
Shell腳本:包含一些命令或聲明与帆,并符合一定格式的文本文件
格式要求:首行shebang機制
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
2.3 Shell腳本創(chuàng)建過程
第一步:使用文本編輯器來創(chuàng)建文本文件
- 第一行必須包括Shell聲明序列:#!
示例:
#! /bin/bash
- 添加注釋,注釋以#開頭
第二步:加執(zhí)行權(quán)限
- 給予執(zhí)行權(quán)限,在命令行上指定腳本的絕對或相對路徑
第三步:運行腳本
- 直接運行解釋器墨榄,將腳本作為解釋器程序的參數(shù)運行
2.4 Shell 腳本注釋規(guī)范
1玄糟、第一行一般為調(diào)用使用的語言
2、程序名: 避免更改文件名后無法找到正確的文件
3袄秩、版本號
4阵翎、更改后的時間
5逢并、作者相關(guān)信息
6、該程序的作用郭卫,及注意事項
7筒狠、最后是各版本的更新簡要說明
2.5 第一個Shell腳本
hello.sh
#!/bin/bash
echo "Hello World"
執(zhí)行腳本方式
通過bash命令可以直接執(zhí)行腳本, 無需給腳本賦予執(zhí)行權(quán)限
[02:46:25 root@CentOS7 ~/scripts]#bash hello.sh
Hello World
[02:46:39 root@CentOS7 ~/scripts]#cat hello.sh | bash
Hello World
[02:46:50 root@CentOS7 ~/scripts]#bash < hello.sh
Hello World
可以使用wget或者curl命令從網(wǎng)站上把腳本下載到服務(wù)器, 然后傳給bash執(zhí)行
將腳本本身內(nèi)容作為標準輸入, 傳給bash
cat test.sh | bash
bash < test.sh
curl http://www.xxx.com/test.sh | bash
[root@demo-c8 ~]# curl -s http://www.xxx.com/testdir/hello.sh | bash
hello, world
Hello, world!
不通過bash命令執(zhí)行, 那么就需要給腳本添加執(zhí)行權(quán)限:
1. 將腳本文件所在路徑加入到PATH變量, 即可直接執(zhí)行, 直接寫腳本文件名稱, 無需指定路徑
2. 將腳本放在PATH變量路徑里, 也可直接執(zhí)行, 直接寫腳本文件名稱, 無需指定路徑
3. 如果沒有把腳本放到PATH里, 就要指定腳本的絕對路徑或者相對路徑
案例: 備份/etc整個目錄到/tmp目錄下
#!/bin/bash
GREEN="echo -e \033[1;31m"
END="\033[0m"
${GREEN}Starting to back up...${END}
sleep 5
cp -a /etc /tmp/etc_`date +%F_%T` #/tmp/etc_`date +%F_%T`, 目標目錄或者文件會自動按照時間創(chuàng)建
[ `echo $?` = 0 ] && echo "Backup Complete" || echo "Backup Failed"
2.6 Shell腳本調(diào)試
- 語法錯誤: 會導(dǎo)致后續(xù)的命令不繼續(xù)執(zhí)行, 可以用bash -n檢查錯誤, 不過提示的出錯行不一定是準確的
- 命令錯誤: 后續(xù)的命令還會繼續(xù)執(zhí)行, 用bash -n無法檢查出來, 可以用bash -x進行觀察, 因此, 執(zhí)行腳本前一定要確保腳本無誤, 尤其是執(zhí)行數(shù)據(jù)備份時. 因為, 一旦出錯可能會丟失數(shù)據(jù)
- 邏輯錯誤: 只能使用bash -x進行觀察, 顯示每一步詳細的執(zhí)行過程, 有助于排錯, 但是bash -x會真正執(zhí)行腳本
2.7 變量
2.7.1 變量
變量表示命名的內(nèi)存空間, 將數(shù)據(jù)放在內(nèi)存空間中, 通過變量名引用, 獲取數(shù)據(jù)
傳統(tǒng)開發(fā)語言, 需要先聲明變量類型, 再賦值, 使用變量
Shell無需對變量聲明類型, 默認對所有變量都當成字符串, 直接賦值變量, 即可使用變量
2.7.2 變量類型
變量類型:
- Shell內(nèi)置變量,如:PS1箱沦,PATH,UID雇庙,HOSTNAME谓形,$$,BASHPID疆前,PPID寒跳,$?,HISTSIZE
- 用戶自定義變量: 用戶在腳本中根據(jù)需求自定義
不同的變量存放的數(shù)據(jù)不同, 取決于以下因素:
- 數(shù)據(jù)存儲方式
- 參與的運算
- 表示的數(shù)據(jù)范圍
變量數(shù)據(jù)類型:
- 字符
- 數(shù)值: 整型, 浮點型, 注意, bash不支持浮點數(shù)
2.7.3 編程語言分類
靜態(tài)和動態(tài)語言
- 靜態(tài)編譯語言:使用變量前竹椒,先聲明變量類型童太,之后類型不能改變,在編譯時檢查胸完,如:Java书释,C, Go
- 動態(tài)編譯語言:不用事先聲明赊窥,可隨時改變類型爆惧,如:bash,Python
強類型和弱類型語言
- 強類型語言:不同數(shù)據(jù)類型間運算操作锨能,必須經(jīng)過強制轉(zhuǎn)換為同一類型才能運算扯再,如Java , C# 址遇,Python
如:參考以下 Python 代碼
print('aaa'+ 10) 提示出錯熄阻,不會自動轉(zhuǎn)換類型
print('aaa'+str(10)) 結(jié)果為aaa10,需要顯示轉(zhuǎn)換類型
- 弱類型語言:語言的運行會隱式做數(shù)據(jù)類型轉(zhuǎn)換倔约。無須指定類型秃殉,默認均為字符型;參與運算會自動進行隱式類型轉(zhuǎn)換跺株;變量無須事先定義可直接調(diào)用
如:bash 复濒,php,Javascript
2.7.4 Shell 中變量命名規(guī)則
- 不能使用程序中的保留字:如:if, for
- 只能使用數(shù)字乒省、字母及下劃線巧颈,且不能以數(shù)字開頭,注意:變量名不支持短橫線 “ - ”袖扛,和主機名相反, 主機名不支持下劃線
- 見名知義砸泛,用英文單詞命名十籍,并體現(xiàn)出實際作用,不要用簡寫唇礁,如:ATM
- 統(tǒng)一命名規(guī)則:駝峰命名法, studentname,大駝峰StudentName, 小駝峰studentName
- 環(huán)境變量名字大寫:STUDENT_NAME
- 局部變量小寫
- 函數(shù)名小寫
2.7.5 變量的定義和飲用
按照變量的生效范圍可劃分為:
環(huán)境變量: 生效范圍為當前Shell進程及其子進程勾栗,如$PATH, $PS1等系統(tǒng)環(huán)境變量, 也可以自定義環(huán)境變量
普通變量: 生效范圍為當前Shell進程;對當前Shell之外的其它Shell進程盏筐,包括當前Shell的子Shell都不生效. 一般用在Shell終端或者腳本中定義.
在腳本中定義的普通變量是無法在Shell終端調(diào)用的
本地變量: 也叫局部變量, 生效范圍為當前Shell進程中某代碼片斷围俘,通常指函數(shù), 一般定義在函數(shù)內(nèi)
變量賦值
Shell的變量賦值要求變量名, 等號和變量值之間不能有空格
name=value
value可是以下多種形式
1. 字符串:name='root'
2. 變量引用:name="$USER"
[03:23:31 root@CentOS7 ~]#name="$SHELL"
[03:25:06 root@CentOS7 ~]#echo $name
/bin/bash
3. 命令引用:name=`COMMAND` 或者 name=$(COMMAND)
[03:25:09 root@CentOS7 ~]#name=`pwd`
[03:25:36 root@CentOS7 ~]#echo $name
/root
注意:變量賦值是臨時生效的,退出終端后琢融,變量會自動刪除界牡,無法持久保存,腳本中的變量會隨著腳本結(jié)束漾抬,也會自動刪除
變量引用
$name
${name}: 通過花括號, 限定變量的邊界
弱引用和強引用
"$name" 弱引用宿亡,其中的變量引用會被替換為變量值
'$name' 強引用,其中的變量引用不會被替換為變量值纳令,而保持原字符串
- 范例:變量的各種賦值方式和引用
[03:25:41 root@CentOS7 ~]#TITLE='CEO'
[03:29:58 root@CentOS7 ~]#echo $TITLE
CEO
[03:30:02 root@CentOS7 ~]#echo i am $TITLE
i am CEO
[03:30:11 root@CentOS7 ~]#echo 'i am $TITLE' # 單引號不會擴展變量
i am $TITLE
[03:30:31 root@CentOS7 ~]#echo "i am $TITLE" # 雙引號會擴展變量
i am CEO
[03:31:21 root@CentOS7 ~]#echo `echo $TITLE` # 反引號既擴展變量也擴展命令
CEO
[03:30:38 root@CentOS7 ~]#echo `i am $TITLE` # $TITLE被反引號擴展為CEO, 反引號內(nèi)部的字符串會被當作命令執(zhí)行, 只是i am CEO不是命令, 所以才會報錯
-bash: i: command not found
[03:31:53 root@CentOS7 ~]#NAME=$USER
[03:33:56 root@CentOS7 ~]#echo $NAME
root
[03:34:01 root@CentOS7 ~]#USER=`whoami`
[03:34:09 root@CentOS7 ~]#echo $USER
root
[03:34:12 root@CentOS7 ~]#FILE=`ls /run`
[03:34:46 root@CentOS7 ~]#echo $FILE
auditd.pid console crond.pid cron.reboot cryptsetup dbus faillock initramfs lock log mount netreport NetworkManager plymouth sepermit setrans sshd.pid sudo syslogd.pid systemd tmpfiles.d tuned udev user utmp vmware
[03:34:48 root@CentOS7 ~]#FILE=/etc/* # 用空格隔開的連續(xù)字符串, 會被依次賦值給變量, 并且也是以空格隔開
[03:35:21 root@CentOS7 ~]#echo $FILE
/etc/adjtime /etc/aliases /etc/aliases.db ...
[03:37:13 root@CentOS7 ~]#seq 10
1
2
3
4
5
6
7
8
9
10
[03:37:14 root@CentOS7 ~]#NUM=`seq 10`
[03:37:19 root@CentOS7 ~]#echo $NUM
1 2 3 4 5 6 7 8 9 10 # 列賦值會被轉(zhuǎn)為行賦值
[03:37:35 root@CentOS7 ~]#NAME="david
> wang
> hahaha"
[03:38:45 root@CentOS7 ~]#echo $NAME
david wang hahaha
[03:39:00 root@CentOS7 ~]#echo "$NAME" # 如果想要列賦值仍然返回列賦值, 那么就用雙引號把變量擴起來
david
wang
hahaha
顯示多個變量:
[23:06:56 root@c8prac ~]#type=it
[23:07:56 root@c8prac ~]#book=linux
[23:08:08 root@c8prac ~]#echo $type:$book
it:linux
不能用下劃線連接, 因為Shell定義變量是可以用下劃線的, 如果兩個變量中間用下劃線連接,那么Shell會認為type_是個變量, 而不是type是一個變量
[23:08:21 root@c8prac ~]#echo $type_$book
linux
解決方法: 利用花括號, 指定變量名的邊界. 建議腳本中的變量盡量用花括號, 避免產(chǎn)生問題
[23:10:48 root@c8prac ~]#echo ${type}_$book
it_linux
可以用橫線連接, 因為Shell定義變量是不能用橫線的, 因此Shell認為type是一個變量, book是一個變量, 而橫線就是一個連接符
[23:09:30 root@c8prac ~]#echo $type-$book
it-linux
變量賦值案例:
將一個變量A的結(jié)果賦值給另一個變量B后, 此時如果修改變量A的值, 那么變量B的值不變.
[23:12:48 root@c8prac ~]#name=david
[23:16:27 root@c8prac ~]#title=$name
[23:16:52 root@c8prac ~]#echo $title
david
[23:16:57 root@c8prac ~]#name=linux
[23:17:04 root@c8prac ~]#echo $title
david
原因:
一個變量名和值做了捆綁之后, 變量名就指向了該值所在的內(nèi)存空間. 除非針對這個變量名重新做賦值或者把原有值刪除, 否則不會變化
單引號, 雙引號, 反引號的區(qū)別
echo 'echo $name' #單引號, 所見即所得, 不會擴展命令和變量
echo $name
echo "echo $name" #雙引號, 只擴展變量, 不會擴展命令. 識別$name為linux后, echo linux并不會被當做命令執(zhí)行, 而是當做字符串, 因此結(jié)果是echo linux
echo linux
echo `echo $name` #反引號, 即擴展命令又擴展變量, 先把$name識別為linux, 再執(zhí)行echo linux命令得到linux, 最后執(zhí)行echo linux得到linux.
linux
動態(tài)命令
將命令賦值給變量, 執(zhí)行, 在腳本中常用
把一個命令, 放到一個變量里, 通過$變量名
, 達到執(zhí)行效果
由于命令是存在變量里的, 而命令是可以修改的, 因此達到了動態(tài)命令的效果
動態(tài)命令中, 如果要引用變量, 那么要用雙引號
[23:25:58 root@c8prac ~]#CMD='hostname'
[23:36:52 root@c8prac ~]#$CMD
c8prac.linux
動態(tài)命令 vs 別名
別名在交互式環(huán)境使用, 而動態(tài)命令是在腳本中使用
別名在腳本中是不生效的
- 多行字符賦值給變量, 并且顯示為多行
[23:40:56 root@c8prac ~]#COURSE="it
> math
> music
> sport
> food"
[23:48:06 root@c8prac ~]#echo $COURSE
it math music sport food
[23:48:12 root@c8prac ~]#echo "$COURSE"
it
math
music
sport
food
變量追加, 在變量原有基礎(chǔ)上, 追加字符串
[23:48:17 root@c8prac ~]#NAME=Michael
[23:51:05 root@c8prac ~]#echo $NAME
Michael
[23:51:09 root@c8prac ~]#NAME+=:Jordan
[23:51:17 root@c8prac ~]#echo $NAME
Michael:Jordan
[root@demo-c8 ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@demo-c8 ~]# PATH+=:/opt
[root@demo-c8 ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/opt
等價于重新賦值
[23:51:20 root@c8prac ~]#NAME=Michael
[23:52:50 root@c8prac ~]#echo $NAME
Michael
[23:52:54 root@c8prac ~]#NAME=$NAME:Jordan
[23:53:06 root@c8prac ~]#echo $NAME
Michael:Jordan
set命令: 顯示自定義變量, 系統(tǒng)內(nèi)置變量, 和函數(shù), 服務(wù)器所有的變量都會顯示
env命令: 只顯示環(huán)境變量
unset命令: 刪除變量. 變量不使用時, 最好把變量刪除. 但是一般在腳本里, 變量都是普通變量, 只在當前進程(腳本)里有效, 腳本執(zhí)行結(jié)束了, 變量也就刪除了.
Shell腳本案例: 打印服務(wù)器基本信息
#!/bin/bash
RED="\E[1;31m"
GREEN="echo -e \E[1;32m"
END="\E[0m"
. /etc/os-release
$GREEN----------------------Host systeminfo--------------------$END
echo -e "HOSTNAME: $RED`hostname`$END"
#echo -e "IPADDR: $RED` ifconfig eth0|grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' |head -n1`$END"
echo -e "IPADDR: $RED` hostname -I`$END"
echo -e "OSVERSION: $RED$PRETTY_NAME$END"
echo -e "KERNEL: $RED`uname -r`$END"
echo -e "CPU: $RED`lscpu|grep '^Model name'|tr -s ' '|cut -d : -f2`$END"
echo -e "MEMORY: $RED`free -h|grep Mem|tr -s ' ' : |cut -d : -f2`$END"
echo -e "DISK: $RED`lsblk |grep '^sd' |tr -s ' ' |cut -d " " -f4`$END"
$GREEN---------------------------------------------------------$END
[root@demo-c8 ~]# bash sysinfo.sh
----------------------Host systeminfo--------------------
HOSTNAME: demo-c8.demo
IPADDR: 10.0.0.108 192.168.122.1
OSVERSION: CentOS Linux 8 (Core)
KERNEL: 4.18.0-193.el8.x86_64
CPU: Intel(R) Core(TM) i7-10875H CPU @ 2.30GHz
MEMORY: 7.6Gi
DISK: 100G
---------------------------------------------------------
練習:
- 編寫腳本
systeminfo.sh
, 顯示當前主機系統(tǒng)信息, 包括: 主機名, ipv4地址, 操作系統(tǒng)版本, 內(nèi)核版本, cpu型號, 內(nèi)存大小, 硬盤大小
編寫腳本
backup.sh
, 可實現(xiàn)每日將/etc
目錄備份到目錄/backup/etcYYYY-mm-dd
中編寫腳本
disk.sh
, 顯示當前硬盤分區(qū)中空間利用率最大的值編寫腳本
links.sh
, 顯示正連接本機的每個遠程主機的ipv4地址和連接數(shù), 并按連接數(shù)從大到小排序
2.7.6 環(huán)境變量
- 父進程和子進程
Linux的進程可以進程嵌套, 在進程1中開啟的其他進程都屬于進程1的子進程
范例: 在f1.sh腳本中, 調(diào)用f2.sh
# f1.sh
[root@demo-c8 data]# vim f1.sh
#!/bin/bash
echo f1.sh
./f2.sh
# f2.sh
[root@demo-c8 data]# vim f2.sh
#!/bin/bash
echo f2.sh
sleep 100
[root@demo-c8 data]# ll
total 8
-rw-r--r--. 1 root root 35 Sep 8 12:06 f1.sh
-rw-r--r--. 1 root root 33 Sep 8 12:05 f2.sh
[root@demo-c8 data]# chmod +x *
[root@demo-c8 data]# ll
total 8
-rwxr-xr-x. 1 root root 35 Sep 8 12:06 f1.sh
-rwxr-xr-x. 1 root root 33 Sep 8 12:05 f2.sh
[root@demo-c8 data]#./f1.sh
f1.sh
f2.sh
├─sshd(1150)─┬─sshd(3479)───sshd(3484)───bash(3492)───pstree(3622)
│ └─sshd(3564)───sshd(3568)───bash(3581)───f1.sh(3619)───f2.sh(3620)───sleep(3621)
- 普通變量只在當前Shell有效, 環(huán)境變量在當前Shell以及其子Shell,子子Shell都有效.
范例: 普通變量只在當前Shell有效
# f1.sh
[root@demo-c8 data]# vim f1.sh
#!/bin/bash
name="admin"
echo f1.sh
echo f1.sh:$name
./f2.sh
# f2.sh
[root@demo-c8 data]# vim f2.sh
#!/bin/bash
echo f2.sh
echo f2.sh:$name # $name是在f2.sh的父進程, f1.sh中定義的, 因此, 無法傳遞給子進程
sleep 100
[root@demo-c8 data]# ./f1.sh
f1.sh
f1.sh:admin
f2.sh
f2.sh:
如果想讓父進程定義的變量, 傳遞給子進程, 那么需要把變量定義為環(huán)境變量
父進程定義的環(huán)境變量可以傳給子進程和子子進程, 但是子進程定義的環(huán)境變量無法傳給父進程
范例: 將name
普通變量定義為環(huán)境變量, 傳遞給子進程
# f1.sh
[root@demo-c8 data]# vim f1.sh
#!/bin/bash
export name="admin"
echo f1.sh
echo f1.sh:$name
./f2.sh
# f2.sh
[root@demo-c8 data]# vim f2.sh
#!/bin/bash
echo f2.sh
echo f2.sh:$name
sleep 100
[root@demo-c8 data]# ./f1.sh
f1.sh
f1.sh:admin
f2.sh
f2.sh:admin
一旦在子進程修改了從父進程繼承的環(huán)境變量, 那么將會把新的值傳遞給孫子進程
范例: 在子進程f2.sh
中, 修改name
變量的值, 驗證會把新的值傳給孫子進程f3.sh
, 同時父進程不受影響, 但是f2.sh本身會受影響
# f1.sh
[root@demo-c8 data]# vim f1.sh
#!/bin/bash
export name="admin" # 父進程中, 將name定義為環(huán)境變量, 值為admin
echo f1.sh
echo f1.sh:$name
./f2.sh
# f2.sh
[root@demo-c8 data]# vim f2.sh
#!/bin/bash
echo f2.sh
echo f2.sh:$name # 子進程可以繼承父進程中的環(huán)境變量
name="root" # 子進程也可修改從父進程繼承的環(huán)境變量
echo f2.sh:$name # 修改后, 子進程會受到影響
./f3.sh
# f3.sh
[root@demo-c8 data]# vim f3.sh
#!/bin/bash
echo f3.sh
echo $name # 孫子進程也會從父進程繼承環(huán)境變量, 但是如果子進程修改了環(huán)境變量, 那么孫子進程的環(huán)境變量會收到影響
sleep 100
[root@demo-c8 data]# ./f1.sh
f1.sh
f1.sh:admin
f2.sh
f2.sh:admin
f2.sh:root
f3.sh
root
一般只在系統(tǒng)配置文件中使用環(huán)境變量, 因為操作系統(tǒng)運行時會產(chǎn)生很多子進程, 但只有一個祖宗進程, 所以需要環(huán)境變量來傳遞值, 但是在腳本中較少使用, 因為腳本很少會嵌套其他腳本
變量聲明和賦值: 臨時聲明環(huán)境變量
#自定義環(huán)境變量, 并且聲明和賦值
export name=VALUE
declare -x name=VALUE
#或者分兩步實現(xiàn)
name=VALUE
export= name
環(huán)境變量在腳本中很少定義, 因為腳本中很少會嵌套其他腳本
一般定義在系統(tǒng)中, 比如PATH變量, 這些變量在所有Shell進程中都會用到
環(huán)境變量一旦在文件中聲明, 后續(xù)直接使用即可, 無需再次聲明
范例: 永久聲明環(huán)境變量
# 將自定義的環(huán)境變量寫到bash的配置文件里, 之后source或者重新登錄終端
全局配置: 對于所有登錄到終端的用戶都有效
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
個人配置: 只對當前登錄終端的用戶有效
~/.bash_profile
~/.bashrc
顯示當前終端進程編號
echo $$
或者
echo $BASHPID
顯示父進程ID
echo $PPID
常用環(huán)境變量
_ #下劃線表示前一個命令的最后一個參數(shù), 如果命令沒有參數(shù), 那就是命令本身
[23:55:49 root@c8prac ~]#ls /data
prac scripts testing_scripts
[23:56:04 root@c8prac ~]#echo $_
/data
顯示所有環(huán)境變量
env
printenv
export
declare -x
刪除變量
unset 變量名
bash內(nèi)建的環(huán)境變量
PATH
SHELL
USER
UID
HOME
PWD
SHLVL # Shell的嵌套層數(shù), 即深度
LANG
MAIL
HOSTNAME
HOSTSIZE
_ # 下劃線表示前一個命令的最后一個參數(shù)
2.7.7 只讀變量
只讀變量: 只能聲明定義, 但后續(xù)不能修改和刪除, 即常量
聲明只讀變量: 當前Shell臨時有效
readonly name
declare -r name
查看只讀變量:
readonly [-p] #不加參數(shù)就是顯示所有只讀變量, 比如UID, BASHPID等.
UID是只讀變量, 其代表的是登錄到終端的用戶身份, 如果UID可以隨意被修改, 那么用戶身份也就可以被隨意修改了
declare -r
范例:
readonly X=3 #只讀變量就是把一個變量定義為常亮, 之后無法修改也無法刪除. 想刪除就要退出當前Shell
[22:47:49 root@centos8 ~]#readonly PI=3
[23:36:35 root@centos8 ~]#readonly PI=4
-bash: PI: readonly variable
[23:36:40 root@centos8 ~]#PI=5
-bash: PI: readonly variable
[22:47:49 root@centos8 ~]#readonly PI=3
[23:36:35 root@centos8 ~]#readonly PI=4
-bash: PI: readonly variable
[23:36:40 root@centos8 ~]#PI=5
-bash: PI: readonly variable
[23:37:00 root@centos8 ~]#echo $PI
3
[23:37:16 root@centos8 ~]#logout
Connection to 192.168.192.8 closed.
david@davids-MacBook-Pro ~ % ssh root@192.168.192.8
root@192.168.192.8's password:
Last login: Wed Sep 7 22:47:49 2022 from 192.168.192.1
[23:37:35 root@centos8 ~]#echo $PI
[23:37:39 root@centos8 ~]#
2.7.8 位置變量
位置變量: 在bash shell中內(nèi)置的變量, 可以在腳本代碼中調(diào)用命令行傳遞給腳本的參數(shù)
$1, $2,... 對應(yīng)第1個, 第2個等參數(shù), shift [n] 換位置
$0, 腳本文件本身, 包括腳本路徑, 如果不想包括路徑, `basename $0`
$* 傳遞給腳本的所有參數(shù), 并且把全部參數(shù)何為一個字符串
$@ 傳遞給腳本的所有參數(shù), 但是把每個參數(shù)為獨立字符串
$# 傳遞給腳本的參數(shù)的個數(shù)
注意: $@ $*只有在被雙引號括起來的時候才會有差異
范例: 位置參數(shù)范例
[root@demo-c8 ~]# vim /data/test.sh
#!/bin/bash
echo $0
basename $0
echo $1
echo $2
echo $3
echo $4
echo $*
echo $@
echo $#
[root@demo-c8 ~]# /data/test.sh 1 2 3 4 5
/data/test.sh
test.sh
1
2
3
4
1 2 3 4 5
1 2 3 4 5
5
[root@demo-c8 ~]# bash /data/test.sh 1 2 3 4 5
/data/test.sh
test.sh
1
2
3
4
1 2 3 4 5
1 2 3 4 5
5
[root@demo-c8 ~]# cd /data
[root@demo-c8 data]# ./test.sh 1 2 3 4 5
./test.sh
test.sh
1
2
3
4
1 2 3 4 5
1 2 3 4 5
5
-
@區(qū)別演示
$*: 把腳本的參數(shù)當做一個整體, 一個字符串
[00:26:39 root@c8prac /data/scripts]#vim test_var1.sh
#!/bin/bash
echo "$*"
test_var2.sh "$*"
[00:26:14 root@c8prac /data/scripts]#vim test_var2.sh
#!/bin/bash
echo $1
[00:28:38 root@c8prac /data/scripts]#bash -x test_var1.sh a b c
+ echo 'a b c'
a b c
+ test_var2.sh 'a b c'
a b c
$@: 把腳本的每一個參數(shù), 當成單獨的一個字符串
[00:28:43 root@c8prac /data/scripts]#vim test_var1.sh
#!/bin/bash
echo "$@"
test_var2.sh "$@"
[00:30:44 root@c8prac /data/scripts]#vim test_var2.sh
#!/bin/bash
echo $1
[00:30:56 root@c8prac /data/scripts]#bash -x test_var1.sh a b c
+ echo a b c
a b c
+ test_var2.sh a b c
a
注意: $@ 和 $*只有在被雙引號""括起來的時候才會有差異
- 腳本位置參數(shù)舉例:
實現(xiàn)安全刪除命令: 將刪除的文件, 移動到/tmp文件夾
[00:30:59 root@c8prac /data/scripts]#mkdir dir1
[00:35:39 root@c8prac /data/scripts]#touch dir1/test1.txt
[00:35:47 root@c8prac /data/scripts]#mkdir dir2
[00:35:51 root@c8prac /data/scripts]#touch dir2/test2.txt
[00:35:55 root@c8prac /data/scripts]#mkdir dir3
[00:35:58 root@c8prac /data/scripts]#touch dir3/test3.txt
[00:38:54 root@c8prac /data/scripts]#vim rm.sh
#!/bin/bash
RED='echo -e \033[1;31m'
END='\033[0m'
DIR=/tmp/`date +%F_%T`
mkdir $DIR
mv $* $DIR &> /dev/null && ${RED}$* have been moved to $DIR${END} || ${RED}Please make sure yor really need to delete them! ${END}
[00:37:41 root@c8prac /data/scripts]#alias rm=rm.sh
# 通過給腳本定義別名為rm, 這樣rm -rf強行刪除命令就無法使用, 可避免誤操作, 不過rm -f還是可以執(zhí)行的, 只不過把-f當成了mv命令第一個參數(shù), 而mv命令是不支持-r選項的, 所以rm -rf無法執(zhí)行
# 因此, 修改了別名后, 還是要禁用rm -rf, rm -f命令, 防止誤刪除, 并且不能使用rm原始命令, 否則還是會執(zhí)行刪除命令, \rm
[00:39:41 root@c8prac /data/scripts]#rm dir2
dir2 have been moved to /tmp/2020-10-23_00:39:45
[00:39:45 root@c8prac /data/scripts]#rm dir3
dir3 have been moved to /tmp/2020-10-23_00:39:48
- 位置參數(shù)是無法人為修改的
[01:03:11 root@c8prac /data/scripts]#vim var.sh
#!/bin/bash
echo $1
$1=linux
echo $1
[01:06:03 root@c8prac /data/scripts]#var.sh a b c
a
/data/scripts/var.sh: line 3: a=linux: command not found #修改位置參數(shù)變量后, 會被展開當做命令, 無法執(zhí)行
a
2.7.9 退出狀態(tài)碼
進程/命令執(zhí)行后挽荠,將使用變量$?
保存狀態(tài)碼的相關(guān)數(shù)字,不同的值反應(yīng)前一個命令執(zhí)行成功或失敗平绩,$?
取值范圍 0-255
$?的值為0 #代表成功
$?的值是1到255 #代表失敗
主要用于條件判斷, 配合流程控制
[root@demo-c8 ~]# grep -q root /etc/passwd
[root@demo-c8 ~]# echo $?
0
[root@demo-c8 ~]# id admin
id: ‘a(chǎn)dmin’: no such user
[root@demo-c8 ~]# id admin &> /dev/null && user exists || useradd admin
[root@demo-c8 ~]# id admin
uid=1001(admin) gid=1001(admin) groups=1001(admin)
范例: 檢測網(wǎng)站可用性, 如果失敗, 那么重啟服務(wù)
# 可以配合定時任務(wù), 每一分鐘, 執(zhí)行一次, 失敗的話就連接到服務(wù)器進行重啟
curl -s http://www.xxx.com
if [$? -ne 0]; then
ssh root@x.x.x.x "nginx -s restart"
$?
顯示最后一條命令執(zhí)行結(jié)果, 對于腳本也一樣, 如果想在腳本執(zhí)行結(jié)束退出后, 用$?
判斷腳本執(zhí)行結(jié)果, 可以用$?
, 但是$?
只能顯示腳本中最后一條命令的執(zhí)行結(jié)果
如果一個命令的執(zhí)行結(jié)果, 既有成功也有失敗的, 那么就會返回失敗
[root@demo-c8 ~]# ls /boot /xxx
ls: cannot access '/xxx': No such file or directory
/boot:
config-4.18.0-193.el8.x86_64 initramfs-0-rescue-eaab49a3b5f746ae847c443ec9bc62c4.img loader vmlinuz-0-rescue-eaab49a3b5f746ae847c443ec9bc62c4
efi initramfs-4.18.0-193.el8.x86_64.img lost+found vmlinuz-4.18.0-193.el8.x86_64
grub2 initramfs-4.18.0-193.el8.x86_64kdump.img System.map-4.18.0-193.el8.x86_64
[root@demo-c8 ~]# echo $?
2
[root@demo-c8 ~]# ls /xxx /boot
ls: cannot access '/xxx': No such file or directory
/boot:
config-4.18.0-193.el8.x86_64 initramfs-0-rescue-eaab49a3b5f746ae847c443ec9bc62c4.img loader vmlinuz-0-rescue-eaab49a3b5f746ae847c443ec9bc62c4
efi initramfs-4.18.0-193.el8.x86_64.img lost+found vmlinuz-4.18.0-193.el8.x86_64
grub2 initramfs-4.18.0-193.el8.x86_64kdump.img System.map-4.18.0-193.el8.x86_64
[root@demo-c8 ~]# echo $?
2
自定義退出狀態(tài)碼: 系統(tǒng)默認的狀態(tài)碼為0-255, 0表示前一個命令執(zhí)行成功, 非0表示執(zhí)行失敗
exit NUM: 在腳本中定義退出狀態(tài)碼, 0也可以定制
腳本中, 一旦遇到exit命令, 腳本會立即終止, 終止退出狀態(tài)碼取決于exit命令后面的數(shù)字
如果整個腳本都沒指定退出狀態(tài)碼, 那么整個腳本的退出狀態(tài)碼取決于腳本中執(zhí)行的最后一條命令的狀態(tài)碼
即使是執(zhí)行失敗的命令, 也可以把狀態(tài)碼定義為成功的0
范例: 默認的退出狀態(tài)碼
[root@demo-c8 data]# vim test.sh
#!/bin/bash
echo start
cmd
echo finish
[root@demo-c8 data]# bash test.sh
start
test.sh: line 3: cmd: command not found
finish
[root@demo-c8 data]# echo $?
0 # 腳本的最后一條命令時echo finish, 執(zhí)行成功, 所以返回0
范例: 將失敗的命令的狀態(tài)碼定義為0
[root@demo-c8 data]# vim test.sh
#!/bin/bash
echo start # echo start正常執(zhí)行
cmd # cmd命令不存在, 返回錯誤
exit 0 # exit 0, 腳本直接退出, 返回狀態(tài)碼為0
echo finish # 腳本退出, echo finish不執(zhí)行
[root@demo-c8 data]# bash test.sh
start
test.sh: line 3: cmd: command not found
[root@demo-c8 data]# echo $?
0
舉例:
grep -q root /etc/passwd #grep -q選項無論有沒有匹配結(jié)果, 都不會顯示
echo $? #可以配合$?查看是否匹配成功
0
2.7.10 展開命令行
Shell命令行命令展開順序:
把命令行分成單個命令詞
展開別名
展開大括號的聲明{}
展開波浪符聲明 ~
命令替換$() 和 ``
再次把命令行分成命令詞
展開文件通配*圈匆、?、[abc]等等
準備I/0重導(dǎo)向 <捏雌、>
運行命令
轉(zhuǎn)義符號: , 反斜線會使隨后的字符按照愿意解釋
范例:
ls --help | grep '\-s'
[root@demo-c8 data]# echo your cost: $500.00
your cost: 00.00
[root@demo-c8 data]# echo your cost: \$500.00
your cost: $500.00
!: 歷史命令替換
115 id admin
116 cd /data
117 ls
118 vim test.sh
119 bash test.sh
120 echo $?
121 vim test.sh
122 bash test.sh
123 echo $?
124 vim test.sh
125 echo $?
126 bash test.sh
127 echo $?
128 echo your cost: $500.00
129 echo your cost: \$500.00
130 cd
131 history
[root@demo-c8 ~]# !115
id admin
uid=1001(admin) gid=1001(admin) groups=1001(admin)
2.7.11 腳本安全和set
set命令: 可以用來定制Shell環(huán)境
$-變量:
h:hashall臭脓,打開選項后,Shell 會將執(zhí)行的命令hash下來腹忽,避免每次都要查詢来累。通過set +h將h選項關(guān)閉
i:interactive-comments,包含這個選項說明當前的 shell 是一個交互式的 shell窘奏。所謂的交互式shell, 在腳本中嘹锁,i選項是關(guān)閉的
m:monitor,打開監(jiān)控模式着裹,就可以通過Job control來控制進程的停止领猾、繼續(xù),后臺或者前臺執(zhí)行等
B:braceexpand骇扇,大括號擴展, {}, 開始時, {}會擴展, 關(guān)閉時, {}不會擴展
H:history摔竿,H選項打開,可以展開歷史列表中的命令少孝,可以通過!感嘆號來完成继低,例如“!!”返回上最近的一個歷史命令,“!n”返回第 n 個歷史命令. 關(guān)閉H選項后, 仍然可以執(zhí)行history命令查看命令歷史, 但是無法通過!調(diào)用歷史命令, 會提示command not found
范例: $-變量
[root@demo-c8 ~]# echo $-
himBHs
變量安全隱患:
一旦某個變量$VAR
被不小心刪除了, 但是后續(xù)還在命令或者腳本中引用, 就會返回空
這時如果執(zhí)行了rm -rf $VAR/*
等類似有安全隱患的命令, 就會有問題
set命令實現(xiàn)腳本安全:
[root@demo-c8 ~]# help set
-u 在擴展一個沒有設(shè)置的變量時, 顯示錯誤信息, 等同set -o nounset. 而且后續(xù)命令不會執(zhí)行, 腳本直接退出.
-e 如果一個命令返回一個非0退出狀態(tài)值(失敗)就退出, 等同set -o errexit. 這樣后續(xù)命令不再執(zhí)行, 默認情況, 當一個命令返回非0狀態(tài), 后續(xù)命令還會執(zhí)行
-o option 顯示, 打開或者關(guān)閉某個選項
顯示選項: set -o
打開選項: set -o 選項
關(guān)閉選項: set +o 選項
-x 當執(zhí)行命令時, 打印命令及其參數(shù), 類似bash -x
范例: 在腳本的第一行, 添加set -ue
實現(xiàn)腳本安全
set -ue
2.8 格式化輸出 printf
內(nèi)部命令
格式: 通過指定的格式模板, 將后續(xù)的文本轉(zhuǎn)換成指定的格式
printf "指定的格式" "文本1" "文本2" ... "文本n"
常用格式替換符:
替換符 功能
%s 字符串
%f 浮點格式
%b 相對應(yīng)的參數(shù)中包含轉(zhuǎn)義字符時稍走,可以使用此替換符進行替換袁翁,對應(yīng)的轉(zhuǎn)義字符會被轉(zhuǎn)義
%c ASCII字符柴底,即顯示對應(yīng)參數(shù)的第一個字符
%d,%i 十進制整數(shù)
%o 八進制值
%u 不帶正負號的十進制值
%x 十六進制值(a-f)
%X 十六進制值(A-F)
%% 表示%本身
說明:
%s 中的數(shù)字代表此替換符中的輸出字符寬度,不足補空格粱胜,默認是右對齊柄驻,%-10s表示10個字符寬,- 表示左對齊
printf默認輸出信息不換行; echo默認換行
如果想讓echo不換行, 需要使用-n選項, echo -n
如果想讓printf換行, 需要使用\n轉(zhuǎn)義字符, printf "\n"
常用轉(zhuǎn)義字符:
轉(zhuǎn)義符 功能
\a 警告字符焙压,通常為ASCII的BEL字符
\b 后退
\f 換頁
\n 換行
\r 回車
\t 水平制表符
\v 垂直制表符
\ 表示\本身
范例: %s
# 將1 2 5 4 8換行顯示
# %s代表一個文本
[11:59:01 root@c8prac ~]#printf "%s\n" 1 2 5 4 8
1
2
5
4
8
# 每兩個文本放在一行顯示
# 每個%s代表一個文本
[root@demo-c8 ~]# printf "%s %s\n" {a..h}
a b
c d
e f
g h
[root@demo-c8 ~]# printf "%s %s\n" {a..i}
a b
c d
e f
g h
i
范例: %f
[root@demo-c8 ~]# printf "%f\n" 1 2 3
1.000000
2.000000
3.000000
[root@demo-c8 ~]# printf "%f %f\n" 1 2 3
1.000000 2.000000
3.000000 0.000000
# .2f表示保留2位小數(shù)
[root@demo-c8 ~]# printf "%.2f\n" 1
1.00
[12:21:48 root@c8prac ~]#printf '(%s)' 1 2 4 5 478 97 #printf默認不換行
(1)(2)(4)(5)(478)(97)[12:22:42 root@c8prac ~]#printf '(%s)' 1 2 4 5 478 97;echo
(1)(2)(4)(5)(478)(97)
[root@demo-c8 ~]# printf "(%s)" 1 2 3 4 5;echo " "
(1)(2)(3)(4)(5)
[root@demo-c8 ~]# printf " (%s) " 1 2 3 4 5;echo ""
(1) (2) (3) (4) (5)
[root@demo-c8 ~]# printf " (%s) " 1 2 3 4 5;echo
(1) (2) (3) (4) (5)
[12:27:00 root@c8prac ~]#VAR="Hello World!"; printf "\033[1;32m%s\033[0m\n" $VAR
Hello
World!
[12:34:14 root@c8prac ~]#VAR="Hello World!"; printf "\033[1;32m%s\033[0m\n" "$VAR" #如果加了雙引號, 就會把前面定義的變量當做一個整體, 因此即使加了\n也不會換行了
Hello World!
# %-10s, 表示左對齊, 默認printf是右對齊
[root@demo-c8 ~]# printf "%10s\n" 1
1
[root@demo-c8 ~]# printf "%-10s\n" 1
1
[root@demo-c8 ~]# printf "%-10s %-10s %-4s %s \n" 姓名 性別 年齡 體重 小明 男 20 70 小紅 女 18 50
姓名 性別 年齡 體重
小明 男 20 70
小紅 女 18 50
范例: 進制轉(zhuǎn)換
# 將十進制的17轉(zhuǎn)換為16進制
[root@demo-c8 ~]# printf "%X\n" 17 # %X表示16進制, [A-F]
11
# 將十六進制的C轉(zhuǎn)換成十進制
[root@demo-c8 ~]# printf "%d \n" 0xC # 16進制以0x開始
12
2.9 算術(shù)運算
Shell允許在某些情況下對算術(shù)表達式進行求值鸿脓,比如:let和declare 內(nèi)置命令,(( ))復(fù)合命令和算術(shù)擴展涯曲。求值以固定寬度的整數(shù)進行答憔,不檢查溢出,盡管除以0會被標記為錯誤掀抹。
運算符及其優(yōu)先級,關(guān)聯(lián)性和值與C語言相同心俗。
以下運算符列表分組為等優(yōu)先級運算符級別傲武。級別按降序排列優(yōu)先。
注意: bash算數(shù)運算只支持整數(shù), 不支持小數(shù)
乘法符號在有些場景中需要進行轉(zhuǎn)義
id++ id-- variable post-increment and post-decrement
++id --id variable pre-increment and pre-decrement
- + unary minus and plus
! ~ logical and bitwise negation
** exponentiation 乘方
* / % multiplication, division, remainder, %表示取模城榛,即取余數(shù)揪利,示例:9%4=1,5%3=2 + - addition, subtraction
<< >> left and right bitwise shifts
<= >= < > comparison
== != equality and inequality
& bitwise AND
^ bitwise exclusive OR
| bitwise OR
&& logical AND
|| logical OR
expr?expr:expr conditional operator
= *= /= %= += -= <<= >>= &= ^= |= assignment
expr1 , expr2 comma
實現(xiàn)算數(shù)運算:
(1) let var=算術(shù)表達式
(2) ((var=算術(shù)表達式)) 和上面等價
(3) var=$[算術(shù)表達式]
(4) var=$((算術(shù)表達式))
(5) var=$(expr arg1 arg2 arg3 ...)
(6) declare -i var = 數(shù)值
(7) echo '算術(shù)表達式' | bc
舉例: RANDOM隨機數(shù)內(nèi)置變量
RANDOM為內(nèi)置隨機數(shù)變量, 每次返回一個隨機數(shù)字, (0-32767)
生成1-50隨機數(shù)
echo $((RANDOM%50+1))
echo $[RANDOM%50+1]
[22:25:34 root@centos-7-1 ~]#((var=RANDOM%50+1))
[22:25:59 root@centos-7-1 ~]#echo $var
15
隨機顏色
# 如果需要把變量引用的結(jié)果, 嵌入到命令中, 那么可以用$((算術(shù)表達式))或者$[算數(shù)表達式]
# 如: echo $[8&4]; echo $((8&4)); 如果用let那么就要寫兩個命令let var=8\&4; echo $var更繁瑣
echo -e "\033[1;$[RANDOM%7+31]mHello World\033[0m" # 內(nèi)容不能有!等運算符號, 會有沖突
引用變量何時需要加$何時不用?
echo -e "\033[1;$[RANDOM%7+31]mHello World\033[0m"
echo -e "\033[1;$[$RANDOM%7+31]mHello World\033[0m"
如果命令能識別一個字符串是變量, 那么就不用加$. 比如在算術(shù)表達式里, 都是數(shù)字, 但是變量是字符, 因此Shell會把字符串認為是變量
注意: 通過算術(shù)運算結(jié)果定義變量時, 一定要用算數(shù)運算符, 否則會被認為是字符串
[13:22:52 root@c8prac ~]#x=10+20
[13:22:59 root@c8prac ~]#echo $x
10+20
[13:23:01 root@c8prac ~]#x=[10+20]
[13:23:08 root@c8prac ~]#echo $x
[10+20]
[13:23:11 root@c8prac ~]#x=$[10+20]
[13:23:21 root@c8prac ~]#echo $x
30
[13:23:23 root@c8prac ~]#let y=10+20
[13:23:41 root@c8prac ~]#echo $y
30
[13:23:43 root@c8prac ~]#((x=1+2))
[13:24:06 root@c8prac ~]#echo $x
3
expr命令: 專門用來做算數(shù)運算. 但是每個參數(shù)之間要?空格, 而且算數(shù)符號需要轉(zhuǎn)義, 否則有可能會被當做通配符
[13:24:08 root@c8prac ~]#expr 1+2
1+2
[13:25:41 root@c8prac ~]#expr 1 + 2
3
[13:25:45 root@c8prac ~]#expr 1 * 2
expr: syntax error: unexpected argument ‘a(chǎn)naconda-ks.cfg’
[13:25:47 root@c8prac ~]#expr 1 \* 2
2
增強型賦值:
+= i+=10 相當于 i=i+10
-= i-=j 相當于 i=i-j
*=
/=
%=
++ i++,++i 相當于 i=i+1
-- i--,--i 相當于 i=i-1
范例:
[root@demo-c8 ~]# let i=10*2
[root@demo-c8 ~]# echo $i
20
[root@demo-c8 ~]# let i=10**2
[root@demo-c8 ~]# echo $i
100
[root@demo-c8 ~]# ((j=1+2))
[root@demo-c8 ~]# echo $j
3
[root@demo-c8 ~]# i=10
[root@demo-c8 ~]# let i+=20
[root@demo-c8 ~]# echo $i
30
[root@demo-c8 ~]# j=20
[root@demo-c8 ~]# let i*=j
[root@demo-c8 ~]# echo $i
600
i++和++i區(qū)別
# i++和++i的執(zhí)行結(jié)果都是將i自增1
[root@demo-c8 ~]# i=1
[root@demo-c8 ~]# let i++
[root@demo-c8 ~]# echo $i
2
[root@demo-c8 ~]# i=1
[root@demo-c8 ~]# let ++i
[root@demo-c8 ~]# echo $i
2
[13:29:05 root@c8prac ~]#i=1;let j=i++; echo "i=$i,j=$j" #i++先賦值, 再自增
i=2,j=1
[13:29:12 root@c8prac ~]#unset i j;i=1;let j=++i; echo "i=$i,j=$j" #++i先自增, 再賦值
i=2,j=2
雞兔同籠:
#!/bin/bash
HEAD=$1
FOOT=$2
RABBIT=$[(FOOT-HEAD-HEAD)/2]
CHOOK=$[HEAD-RABBIT]
printf "%-10s %-10s \n" CHOOK:$CHOOK RABBIT:$RABBIT
[13:42:35 root@c8prac ~]#bash chook_rabbit.sh 35 100
CHOOK:20 RABBIT:15
范例: 計算結(jié)果保留3位小數(shù), 默認情況, Shell計算不會保留小數(shù), 只保留整數(shù)部分, 也不會四舍五入
[root@demo-c8 ~]# let var=20/3
[root@demo-c8 ~]# echo $var
6
# bc默認也不支持小數(shù), 需要通過scale指定保留幾位小數(shù)
[root@demo-c8 ~]# echo "scale=3; 20/3" | bc
6.666
2.10 邏輯運算
邏輯運算中的0和1都是二進制數(shù), 表示真假, 而$?
退出狀態(tài)碼的0-255是十進制數(shù), 0表示上一條命令執(zhí)行成功, 非0表示上一條命令執(zhí)行失敗, 兩者不同
真;假
true,false
1;0
與運算: & 任何數(shù)與0相與, 結(jié)果為0, 任何數(shù)與1相與, 結(jié)果為原值
1 與 1 = 1
1 與 0 = 0
0 與 1 = 0
0 與 0 = 0
范例: 十進制數(shù)與運算
# 十進制數(shù)與運算, 需要把十進制轉(zhuǎn)換成二進制, 之后, 針對每一位二進制數(shù)進行與運算, 再將最后的結(jié)果轉(zhuǎn)為十進制
[root@demo-c8 data]# let var=8\&8
[root@demo-c8 data]# echo $var
8
[root@demo-c8 data]# let var=8\&4
[root@demo-c8 data]# echo $var
0
[root@demo-c8 data]# let var=1\&1
[root@demo-c8 data]# echo $var
1
[root@demo-c8 data]# let var=1\&0
[root@demo-c8 data]# echo $var
0
或運算: | 任何數(shù)和1或, 結(jié)果為1, 和0或結(jié)果為原值
1 或 1 = 1
1 或 0 = 1
0 或 1 = 1
0 或 0 = 0
范例: 十進制數(shù)或運算
[root@demo-c8 data]# echo $((8|4))
12
非運算: !
! 1 = 0 ! true=false
! 0 = 1 ! false=true
異或運算: 相同為假, 不同為真
異或的兩個值狠持,相同為假疟位,不同為真。兩個數(shù)字X,Y異或得到結(jié)果Z喘垂,Z再和任意兩者之一X異或甜刻,將得出另一個值Y
1 ^ 1 = 0
1 ^ 0 = 1
0 ^ 1 = 1
0 ^ 0 = 0
x^y=z
x^z=y
y^z=x
范例: 異或運算實現(xiàn)變量互換
[13:42:36 root@c8prac ~]#x=1;y=2;x=$[x^y];y=$[x^y];x=$[x^y];echo x=$x,y=$y
x=2,y=1
[23:07:24 root@centos-7-1 ~]#x=10;y=20; temp=$x; x=$y; y=$temp; echo $x, $y
20, 10
短路運算
- 短路與
CMD1 短路與 CMD2
第一個CMD1結(jié)果為真 (1),第二個CMD2必須要參與運算正勒,才能得到最終的結(jié)果
第一個CMD1結(jié)果為假 (0)得院,總的結(jié)果必定為0,因此不需要執(zhí)行CMD2
- 短路或
CMD1 短路或 CMD2
第一個CMD1結(jié)果為真 (1)章贞,總的結(jié)果必定為1祥绞,因此不需要執(zhí)行CMD2
第一個CMD1結(jié)果為假 (0),第二個CMD2 必須要參與運算,才能得到最終的結(jié)果
2.11 條件測試命令
條件測試: 判斷某需求是否滿足, 需要由測試機制來實現(xiàn), 專用的測試表達式需要由測試命令輔助完成測試過程, 實現(xiàn)評估布爾聲明, 以便用在條件性環(huán)境下進行執(zhí)行
測試結(jié)果為真, 則狀態(tài)碼變量$?
返回0
測試結(jié)果為假, 則狀態(tài)碼變量$?
返回1
條件測試命令:
# 表達式前后必須有空白字符, 也就是空格
test 表達式
[] 表達式; 等價于test, 建議用[]
[[]] 表達式; 功能強于test和[]
# 查看條件測試命令幫助
help test
help [
2.11.1 變量測試
判斷變量是否已經(jīng)定義
[ -v VARNAME ]
[00:15:25 root@c7node2 ~]#[ -v NAME ]
[00:19:03 root@c7node2 ~]#echo $?
1
[00:19:05 root@c7node2 ~]#NAME=WANG
[00:19:12 root@c7node2 ~]#[ -v NAME ]
[00:19:14 root@c7node2 ~]#echo $?
0
# 如果變量定義了, 但是沒賦值, 那么也返回0
[root@demo-c8 data]# unset X
[root@demo-c8 data]# X=
[root@demo-c8 data]# [ -v X ]
[root@demo-c8 data]# echo $?
0
2.11.2 數(shù)值測試
-eq 等于
-ne 不等于
-lt 小于
-le 小于等于
-gt 大于
-ge 大于等于
[root@demo-c8 data]# i=1;j=2
[root@demo-c8 data]# [[ $i -eq $j ]]
[root@demo-c8 data]# echo $?
1
[root@demo-c8 data]# i=100;j=200
[root@demo-c8 data]# [ $i -gt $j ]
[root@demo-c8 data]# echo $?
1
[00:22:18 root@c7node2 ~]#x=1;y=2
[00:22:24 root@c7node2 ~]#[ $x -eq $y ] # 條件判斷中, 變量引用要加$
[00:22:26 root@c7node2 ~]#echo $?
1
算數(shù)表達式比較:
<=
>=
<
>
[00:22:28 root@c7node2 ~]#x=1;y=2;(( x > y ));echo $? # 如果用(())進行條件運算, 變量不用加$, 加上也可以
1
2.11.3 字符串測試
test和[]用法
test和 [ ]用法
-z STRING 字符串是否為空鸭限,沒定義或空為真, $?返回0蜕径,不空為假$?返回1
-n STRING 字符串是否不空,不空為真败京,空為假
STRING 同上
STRING1 = STRING2 是否等于兜喻,注意 = 前后有空格
STRING1 != STRING2 是否不等于
> 比較ascii碼編號
< 比較ascii碼編號
范例: 判斷字符串是否為空, 也可判斷變量是否被賦值了
# 判斷是否非空, 如果有值, 那么就是非空的, 返回0
[00:29:26 root@c7node2 ~]#NAME=WANG;[ $NAME ]; echo $?
0
# 如果沒有值, 那么就是空的, 返回1
[00:29:42 root@c7node2 ~]#NAME=WANG;unset NAME;[ $NAME ]; echo $?
1
[00:29:56 root@c7node2 ~]#[ "" ];echo $? #雙引號里面為空, 則結(jié)果為假. 判斷的是雙引號里面的東西, 而不是雙引號本身
1
[root@demo-c8 data]# [ -z " " ]; echo $? # 雙引號內(nèi)是空格, 非空, 所以結(jié)果為假
1
范例: 判斷字符串是否相等
STRING1 = STRING2 # 字符串和括號之間要有空格
the strings are equal
[00:34:46 root@c7node2 ~]#NAME1=WANG;NAME2=LI;[ $NAME1 = $NAME2 ];echo $?
1
[00:35:57 root@c7node2 ~]#NAME1=WANG;NAME2=LI;[ $NAME1 != $NAME2 ];echo $?
0 # != 不等
范例: 在比較字符串時, 建議變量用雙引號括起來
[root@demo-c8 data]# NAME="I love Linux"
[root@demo-c8 data]# [ $NAME ]
-bash: [: love: binary operator expected
[root@demo-c8 data]# [ "$NAME" ]
[root@demo-c8 data]# echo $?
0
[[]]用法
[[ expression ]] 用法
==: 左側(cè)字符串是否和右側(cè)的PATTERN相同
注意: 此表達式用于[[]]中, PATTERN為通配符
=~: 左側(cè)字符串是否能夠被右側(cè)的擴展正則表達式PATTERN所匹配
注意: 此表達式用于[[]]中, 是擴展正則表達式
建議: 當使用正則表達式或者通配符時使用[[]]
, 其他情況一般使用[]
舉例:
PATTERN為通配符: 此時. 如果*
作為通配符, 那么不用加""
, 如果想作為*
本身, 那么就加""
或者轉(zhuǎn)義\
[root@demo-c8 data]# FILE="linux10"
[root@demo-c8 data]# [[ "$FILE" == linux* ]] # 不加雙引號, 那么*就是通配符
[root@demo-c8 data]# echo $?
0
[root@demo-c8 data]# [[ "$FILE" == "linux*" ]] # 加上雙引號, 那么*就是本身意思了, 不是通配符了
[root@demo-c8 data]# echo $?
1
[00:36:04 root@c7node2 ~]#FILE=backup.sh
[00:45:40 root@c7node2 ~]#[[ $FILE == *.sh ]];echo $?
0
[00:46:03 root@c7node2 ~]#[[ $FILE != *.sh ]];echo $?
1
[root@demo-c8 data]# FILE=test.log
[root@demo-c8 data]# [[ "$FILE"==*.log ]]
[root@demo-c8 data]# echo $?
0
[root@demo-c8 data]# [[ "$FILE" == *.sh ]]
[root@demo-c8 data]# echo $?
0
[root@demo-c8 data]# FILE=test.zip
[root@demo-c8 data]# [[ "$FILE" != *.sh ]]
[root@demo-c8 data]# echo $?
0
PATTERN正則表達式: 此時, PATTERN不用加雙引號
[00:46:24 root@c7node2 ~]#FILE=test.log
[00:47:37 root@c7node2 ~]#[[ $FILE =~ \.log$ ]];echo $? # 判斷字符串是否以.log結(jié)尾
0
[root@demo-c8 data]# FILE="test.sh"
[root@demo-c8 data]# [[ "$FILE" =~ \.sh$ ]]
[root@demo-c8 data]# echo $?
0
[root@demo-c8 data]# [[ "$N" =~ ^[0-9]+$ ]]
[root@demo-c8 data]# echo $?
0
[root@demo-c8 data]# M="Linux10"
[root@demo-c8 data]# [[ "$M" =~ ^[0-9]+$ ]]
[root@demo-c8 data]# echo $?
1
注意: 比較字符串時, 建議用雙引號引起來, 把字符串作為一個整體 否則容易出錯
舉例:
[00:48:09 root@c7node2 ~]#NAME="I love Linux"
[00:54:07 root@c7node2 ~]#[ $NAME ] # 相當于執(zhí)行[ I love Linux ], 當做命令執(zhí)行了
-bash: [: love: binary operator expected
[00:54:11 root@c7node2 ~]#[ "$NAME" ] # 相當于執(zhí)行[ "I love Linux" ], 進行字符串判斷
[00:54:16 root@c7node2 ~]#echo $?
0
范例: 判斷一個地址是否為ip地址
2.11.4 文件測試
針對不同的文件類型是否存在, 以及是否為存在的目錄進行判斷
存在性測試:
-a | -e FILE: 判斷文件或者目錄是否存在
-b FILE: 判斷文件是否存在且為塊設(shè)備
-c FILE: 判斷文件是否存在且為字符設(shè)備文件
-d FILE: 判斷文件是否存在且為目錄文件
-f FILE: 判斷文件是否存在且為普通文件
-h | -L: 判讀文件是否存在且為符號鏈接文件
-p FILE: 判斷文件是否存在且為命令管道文件
-S FILE: 判斷文件是否存在且為套接字文件
[00:54:19 root@c7node2 ~]#[ -e /etc/issue ]; echo $?
0
[01:04:20 root@c7node2 ~]#[ -d /etc ]; echo $?
0
[01:04:34 root@c7node2 ~]#[ -h /bin ]; echo $?
0
文件權(quán)限測試:
-r FILE 是否存在且可讀
-w FILE 是否存在且可寫
-x FILE 是否存在且可執(zhí)行
-u FILE 是否存在且擁有suid權(quán)限
-g FILE 是否存在且擁有sgid權(quán)限
-k FILE 是否存在且擁有sticky權(quán)限
注意: 最終結(jié)果由用戶對文件的實際權(quán)限決定, 而非文件本身屬性決定
舉例: shadow文件用于保存用戶密碼, 而passwd程序擁有SUID權(quán)限, 所以任何用戶都是可以繼承root用戶權(quán)限, 去修改和查看shadow文件內(nèi)容
[01:07:40 root@c7node2 ~]#[ -w /etc/shadow ]; echo $?
0
[01:07:46 root@c7node2 ~]#[ -x /etc/shadow ]; echo $? # x權(quán)限必須有x位才行, 即使root賬號沒有x權(quán)限, 也無法執(zhí)行文件
1
[01:07:48 root@c7node2 ~]#[ -r /etc/shadow ]; echo $?
0
范例: 給文件添加了特殊只讀權(quán)限, 那么即使文件本身是可寫的, 那么也會變成無法寫
[root@demo-c8 data]# [ -w /etc/fstab ]
[root@demo-c8 data]# echo $?
0
[root@demo-c8 data]# chattr +i /etc/fstab
[root@demo-c8 data]# lsattr /etc/fstab
----i--------------- /etc/fstab
[root@demo-c8 data]# echo 1 >> /etc/fstab
-bash: /etc/fstab: Operation not permitted
[root@demo-c8 data]# [ -w /etc/fstab ]
[root@demo-c8 data]# echo $?
1
文件屬性測試:
-s FILE #是否存在且非空
-t fd #fd 文件描述符是否在某終端已經(jīng)打開
-N FILE #文件自從上一次被讀取之后是否被修改過
-O FILE #當前有效用戶是否為文件屬主
-G FILE #當前有效用戶是否為文件屬組
FILE1 -ef FILE2 #FILE1是否是FILE2的硬鏈接
FILE1 -nt FILE2 #FILE1是否新于FILE2(mtime)
FILE1 -ot FILE2 #FILE1是否舊于FILE2
2.12 關(guān)于() vs {}
(CMD1;CMD2;...)
和 { CMD1;CMD2;...; }
都可以將多個命令組合在一起,批量執(zhí)行
(CMD1;CMD2;...)
: 開啟子進程, 變量賦值和內(nèi)部命令不會影響當前環(huán)境, 只在子進程生效; 并且小括號里的子進程是可以從其父進程繼承普通變量的. 小括號和命令之間無需空格
[root@demo-c8 ~]# name="admin";echo $BASHPID;(echo $BASHPID;echo ${name};);echo $BASHPID
3662
6854
admin # 子進程6854從父進程3662進程name變量
3662
{ CMD1;CMD2;...; }
: 在當前Shell中執(zhí)行命令, 不開啟子進程. 會影響當前Shell環(huán)境. 花括號和命令之間要有空格
[root@demo-c8 ~]# name="root";echo $BASHPID;{ echo $BASHPID;name="david";echo ${name}; };echo $BASHPID; echo ${name}
3662
3662
david
3662
david
舉例:
[01:18:17 root@c7node2 ~]#hostname;pwd > test.log # 分號只會把pwd這個命令的結(jié)果追加到test.log
c7node2.linux
[01:18:26 root@c7node2 ~]#cat test.log
/root
[01:18:29 root@c7node2 ~]#{ hostname;pwd; } > test.log # 通過{}將兩個命令作為整體一起執(zhí)行, 把兩個命令的結(jié)果都追加給test.log
[01:18:41 root@c7node2 ~]#cat test.log
c7node2.linux
/root
()的實用案例:
范例: 開啟子進程, 臨時切換目錄執(zhí)行命令, 不改變當前工作目錄
[root@demo-c8 ~]# (cd /data;ls;) # 小括號會開啟子進程, 在子進程內(nèi)切換目錄, 在父進程中,還是停留在源目錄
rm.sh test.sh
[root@demo-c8 ~]# pwd
/root
[root@demo-c8 ~]# { cd /data;ls; } # 花括號不會開啟子進程, 會在當前進程切換目錄, 命令執(zhí)行結(jié)束后, 會切換到新的目錄
rm.sh test.sh
[root@demo-c8 data]# pwd
/data
范例: 臨時修改umask, 創(chuàng)建文件
[root@demo-c8 ~]# umask
0022
[root@demo-c8 ~]# (umask 000; touch f1.txt) # 666-000=666
[root@demo-c8 ~]# umask
0022
[root@demo-c8 ~]# ll f1.txt
-rw-rw-rw-. 1 root root 0 Sep 8 19:51 f1.txt
2.13 組合條件測試
2.13.1第一種方式: []
[ EXPRESSION1 -a EXPRESSION2 ] 并且, 1和2都為真, 結(jié)果才為真
[ EXPRESSION1 -o EXPRESSION2 ] 或者, 1和2只要有一個真, 結(jié)果就為真
[ ! EXPRESSION ] 取反
說明: -a 和 -o 需要使用條件測試命令(test
或[]
)進行赡麦,[[ ]]
不支持
舉例:
# test.log是否為普通文件, 并且當前用戶對于該文件是否有執(zhí)行權(quán)限
[01:28:48 root@c7node2 ~]#[ -f test.log -a -x test.log ]
[01:29:05 root@c7node2 ~]#echo $?
1
[01:29:09 root@c7node2 ~]#[ -f test.log -o -x test.log ]
[01:29:41 root@c7node2 ~]#echo $?
0
[root@demo-c8 ~]# ll test.log
-rw-r--r--. 1 root root 6 Sep 8 19:43 test.log
[root@demo-c8 ~]# [ ! -x test.log ]
[root@demo-c8 ~]# echo $?
0
2.13.2 第二種方式: &&和||
短路與 &&
短路或 ||
! CMD 取反
CMD1 && CMD2 || CMD3
: &&要在前面使用
CMD1如果執(zhí)行失敗, 則不執(zhí)行CMD2, 此時CMD1 && CMD2作為整體已經(jīng)為假, 則繼續(xù)執(zhí)行CMD3
如果CMD1執(zhí)行成功, 則執(zhí)行CMD2
[root@demo-c8 ~]# [ -f /bin/cat -a -x /bin/cat ] && cat /etc/fstab
#
# /etc/fstab
# Created by anaconda on Mon Aug 15 16:52:19 2022
#
# Accessible filesystems, by reference, are maintained under '/dev/disk/'.
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info.
#
# After editing this file, run 'systemctl daemon-reload' to update systemd
# units generated from this file.
#
UUID=b1ab1ace-2582-4afd-8693-39bd9855041c / xfs defaults 0 0
UUID=d5131695-82b3-4a23-bc28-5c8a4bf381a0 /boot ext4 defaults 1 2
UUID=bdd66510-e510-4fe7-ba71-e2a35e6dc492 /data xfs defaults 0 0
UUID=05c944fb-d6f9-4544-ba10-8b7bf3cc8fed swap swap defaults 0 0
范例: 如果生成的隨機數(shù)能被6整除, 那么返回"yes", 否則返回"no"
[root@demo-c8 ~]# [ $[$RANDOM%6] -eq 0 ] && echo "yes" || echo "no"
no
[root@demo-c8 ~]# [ $[$RANDOM%6] -eq 0 ] && echo "yes" || echo "no"
no
[root@demo-c8 ~]# [ $[$RANDOM%6] -eq 0 ] && echo "yes" || echo "no"
yes
[root@demo-c8 ~]# [ $[$RANDOM%6] -eq 0 ] && echo "yes" || echo "no"
yes
[root@demo-c8 ~]# [ $[$RANDOM%6] -eq 0 ] && echo "yes" || echo "no"
no
[root@demo-c8 ~]# [ $[$RANDOM%6] -eq 0 ] && echo "yes" || echo "no"
no
[root@demo-c8 ~]# [ $[$RANDOM%6] -eq 0 ] && echo "yes" || echo "no"
no
范例: 簡單的網(wǎng)站健康性檢查
curl 127.0.0.1 &> /dev/null && echo "Host running" || { echo "Host down"; systemctl restart nginx; }
Host down
范例: 網(wǎng)絡(luò)狀態(tài)判斷
#!/bin/bash
for i in {1..254};do
ping -c1 -W1 10.0.0.$i &> /dev/null && echo "10.0.0.$i is up" &> ok.log || echo "10.0.0.$i is down" &> down.log;
done
范例: 磁盤空間利用率判斷
#!/bin/bash
WARNING=80
DISK=`df -h | sed -nr 's#(^\/dev/sda[0-9]{1})(.*)#\1#p'`
for i in $DISK;do
[ `df -h | grep "$i" | tr -s ' ' | cut -d ' ' -f 5 | cut -d '%' -f1` -eq $WARNING ] && echo "$i is bad" || echo "$i is good";
done
范例:磁盤空間和Inode號的檢查腳本
[root@centos8 scripts]#cat disk_check.sh
#!/bin/bash
WARNING=80
SPACE_USED=`df | grep '^/dev/sd'|grep -oE '[0-9]+%'|tr -d %| sort -nr|head -1`
INODE_USED=`df -i | grep '^/dev/sd'|grep -oE '[0-9]+%'|tr -d %| sort -nr|head -1`
[ "$SPACE_USED" -gt $WARNING -o "$INODE_USED" -gt $WARNING ] && echo "DISK USED:$SPACE_USED%, INODE_USED:$INODE_USED,will be full" | mail -s "DISK Warning" root@xxx.com
練習
1虹统、編寫腳本 argsnum.sh弓坞,接受一個文件路徑作為參數(shù);如果參數(shù)個數(shù)小于1车荔,則提示用戶“至少應(yīng)該給一個參數(shù)”渡冻,并立即退出;如果參數(shù)個數(shù)不小于1忧便,則顯示第一個參數(shù)所指向的文件中的空白行數(shù)
2族吻、編寫腳本 hostping.sh,接受一個主機的IPv4地址做為參數(shù)珠增,測試是否可連通超歌。如果能ping通,則提示用戶“該IP地址可訪問”蒂教;如果不可ping通巍举,則提示用戶“該IP地址不可訪問”
3、編寫腳本 checkdisk.sh凝垛,檢查磁盤分區(qū)空間和inode使用率懊悯,如果超過80%,就發(fā)廣播警告空間即將滿了
4梦皮、編寫腳本 per.sh蜓氨,判斷當前用戶對指定參數(shù)文件润文,是否不可讀并且不可寫
5、編寫腳本 excute.sh ,判斷參數(shù)文件是否為sh后綴的普通文件咪惠,如果是惩猫,添加所有人可執(zhí)行權(quán)限嘁灯,否則提示用戶非腳本文件
6辞友、編寫腳本 nologin.sh和 login.sh,實現(xiàn)禁止和允許普通用戶登錄系統(tǒng)
2.13 使用read命令來接受輸入
使用read
來把輸入值分配給一個或多個Shell變量, read從標準輸入中讀取值, 給每個單詞分配一個變量, 所有剩余單詞, 都會被分配給最后一個變量, 如果變量名沒有指定, 默認標準輸入的值會賦值給系統(tǒng)內(nèi)置變量REPLY
格式:
read [options] [name ...]
常見選項:
-p 指定要顯示的提示
-s 靜默輸入溃睹,一般用于密碼
-n N 指定輸入的字符長度N
-d '字符' 輸入結(jié)束符
-t N TIMEOUT為N秒
范例: 將標準輸入存到REPLY變量
范例: 將標準輸入存到指定的變量
范例: 按照提示輸入信息
范例: 利用標準輸入, 執(zhí)行read
# 標準輸入需要用<<<
[root@demo-c8 ~]# read x y z <<< "l love linux"
[root@demo-c8 ~]# echo $x $y $z
l love linux
# 通過管道接收標準輸入
[root@demo-c8 ~]# unset NAME
[root@demo-c8 ~]# echo "admin" | read NAME
[root@demo-c8 ~]# echo $NAME
# 管道會開啟兩個子進程, 管道前后的命令是運行在不同的子進程的, 而echo $NAME運行在父進程, 所以在父進程是拿不到read子進程的NAME變量值的
[root@demo-c8 ~]# echo "admin" | read NAME; echo $NAME
# 通過{}或者[]確保read和echo處在同一個進程
[root@demo-c8 ~]# echo "admin" | { read NAME; echo $NAME; }
admin
[root@demo-c8 ~]# echo "root" | (read NAME;echo $NAME;)
root
范例: 管道開啟兩個子進程
[root@demo-c8 opt]# echo $BASHPID
2099
范例: 在管道后, 接收并返回管道前傳入的值
[root@demo-c8 opt]# echo 1 2 | read x y
[root@demo-c8 opt]# echo $x
[root@demo-c8 opt]# echo $y
[root@demo-c8 opt]#
- read x y和echo
$x
; echo$y
分開寫, 也是處在不同的進程
[root@demo-c8 opt]# echo 1 2 | (read x y; echo $x; echo $y)
1
2
[root@demo-c8 opt]# echo 1 2 | { read x y; echo $x; echo $y; }
1
2
范例: 將變量定義存放到文件中, 之后通過標準輸入, 傳給read
[root@demo-c8 ~]# vim test.txt
1 2
[root@demo-c8 ~]# read i j < test.txt
[root@demo-c8 ~]# echo $i
1
[root@demo-c8 ~]# echo $j
2
范例: 根據(jù)用戶輸入執(zhí)行不同命令
[root@demo-c8 data]# vim read.sh
[root@demo-c8 data]# vim read.sh
#!/bin/bash
read -p "請輸入是否執(zhí)行(y|Y for yes; other keys for no): " ACTION
[[ $ACTION =~ ^[Yy]$ ]] && echo "action" || echo "no action"
[root@demo-c8 data]# bash read.sh
請輸入是否執(zhí)行(y|Y for yes; other keys for no): y
action
[root@demo-c8 data]# bash read.sh
請輸入是否執(zhí)行(y|Y for yes; other keys for no): Y
action
[root@demo-c8 data]# bash read.sh
請輸入是否執(zhí)行(y|Y for yes; other keys for no): n
no action
[root@demo-c8 data]# bash read.sh
請輸入是否執(zhí)行(y|Y for yes; other keys for no): N
no action
[root@demo-c8 data]# bash read.sh
請輸入是否執(zhí)行(y|Y for yes; other keys for no): adad
no action
范例: 實現(xiàn)運維腳本的菜單功能
[root@demo-c8 data]# vim devops.sh
#!/bin/bash
. /etc/init.d/functions
echo -en "\033[$[$RANDOM%7+31];1m"
cat <<EOF
請選擇:
1): 備份所有數(shù)據(jù)庫
2): 清理日志
3): 軟件升級
4): 軟件回滾
5): 刪庫跑路
EOF
echo -en "\033[0m"
while true; do
read -p "請輸入需要執(zhí)行的操作(1,2,...) : " ACTION
[ $ACTION -eq 1 ] && echo "備份所有數(shù)據(jù)庫"
[ $ACTION -eq 2 ] && echo "清理日志"
[ $ACTION -eq 3 ] && echo "軟件升級"
[ $ACTION -eq 4 ] && echo "軟件回滾"
[ $ACTION -eq 5 ] && echo "刪庫跑路"
done