7: Shell腳本基礎(chǔ)

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

編譯和解釋型語言:

圖片.png

編譯型語言不支持跨平臺, 因為編譯型語言的運行需要先把源代碼通過編譯器轉(zhuǎn)換成二進制文件, 最終運行的是二進制文件. 而不同平臺的編譯器將源代碼編譯后的二進制文件格式不同, 因此編譯型語言不支持跨平臺

解釋型語言如Python, 支持跨平臺, 因為解釋型語言是靠解釋器運行源代碼. 如果需要跨平臺, 那么只需要在不同平臺安裝對應(yīng)版本解釋器, 就可以直接運行源代碼

  • Java
圖片.png

Java支持跨平臺, 將源代碼轉(zhuǎn)換成字節(jié)碼后, 把字節(jié)碼移植到不同的操作系統(tǒng), 通過不同操作系統(tǒng)上的Java虛擬機去轉(zhuǎn)換成二進制文件

1.4 編輯邏輯處理方式

順序結(jié)構(gòu)流程:

圖片.png

分支結(jié)構(gòu)流程:

圖片.png

循環(huán)結(jié)構(gòu)流程:

圖片.png

三種處理邏輯:

  • 順序執(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)存空間. 除非針對這個變量名重新做賦值或者把原有值刪除, 否則不會變化

image.png

單引號, 雙引號, 反引號的區(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
---------------------------------------------------------

練習:

  1. 編寫腳本systeminfo.sh, 顯示當前主機系統(tǒng)信息, 包括: 主機名, ipv4地址, 操作系統(tǒng)版本, 內(nèi)核版本, cpu型號, 內(nèi)存大小, 硬盤大小
  1. 編寫腳本backup.sh, 可實現(xiàn)每日將/etc目錄備份到目錄/backup/etcYYYY-mm-dd

  2. 編寫腳本disk.sh, 顯示當前硬盤分區(qū)中空間利用率最大的值

  3. 編寫腳本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變量

image.png

范例: 將標準輸入存到指定的變量

image.png
image.png

范例: 按照提示輸入信息

image.png

范例: 利用標準輸入, 執(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
image.png
image.png

范例: 在管道后, 接收并返回管道前傳入的值

[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分開寫, 也是處在不同的進程
image.png
[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
image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末荐虐,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子丸凭,更是在濱河造成了極大的恐慌福扬,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惜犀,死亡現(xiàn)場離奇詭異铛碑,居然都是意外死亡,警方通過查閱死者的電腦和手機虽界,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門汽烦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人莉御,你說我怎么就攤上這事撇吞∷锥常” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵牍颈,是天一觀的道長迄薄。 經(jīng)常有香客問我,道長煮岁,這世上最難降的妖魔是什么讥蔽? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮画机,結(jié)果婚禮上冶伞,老公的妹妹穿的比我還像新娘。我一直安慰自己步氏,他們只是感情好响禽,可當我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著荚醒,像睡著了一般芋类。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上腌且,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天,我揣著相機與錄音榛瓮,去河邊找鬼铺董。 笑死,一個胖子當著我的面吹牛禀晓,可吹牛的內(nèi)容都是我干的精续。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼粹懒,長吁一口氣:“原來是場噩夢啊……” “哼重付!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起凫乖,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤确垫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后帽芽,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體删掀,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年导街,在試婚紗的時候發(fā)現(xiàn)自己被綠了披泪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡搬瑰,死狀恐怖款票,靈堂內(nèi)的尸體忽然破棺而出控硼,到底是詐尸還是另有隱情,我是刑警寧澤艾少,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布卡乾,位于F島的核電站,受9級特大地震影響姆钉,放射性物質(zhì)發(fā)生泄漏说订。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一潮瓶、第九天 我趴在偏房一處隱蔽的房頂上張望陶冷。 院中可真熱鬧,春花似錦毯辅、人聲如沸埂伦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽步绸。三九已至刑桑,卻和暖如春蜓陌,著一層夾襖步出監(jiān)牢的瞬間隧期,已是汗流浹背拾并。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工季希, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人劳淆。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓乱豆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親论泛。 傳聞我的和親對象是個殘疾皇子揩尸,可洞房花燭夜當晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內(nèi)容