2. Shell 条件测试

发布时间 2023-11-28 20:05:53作者: 谱次·

 

重点:

条件测试。

read。

Shell 环境配置。

case。

for。

find。

xargs。

gzip,bzip2,xz。

tar。

sed。

1)位置 变量

位置变量:在 bash Shell 中内置的变量,在脚本代码中调用,通过命令行传递给脚本的参数。

$1, $2, $3 ... "对应第 1 个、第 2 个、 第 3 个等参数"

$0 "命令名称, 包括路径"

$* "传递给脚本的所有参数" 全部参数合为`一个字符串`

$@ "传递给脚本的所有参数" 每个参数为`独立字符串`

$# "传递给脚本的参数的个数"

注意:$@ $* 只在被双引号包起来的时候才会有差异

位置变量是一个非常灵活和方便的功能。

您可以在运行脚本时,通过 在脚本名后面增加参数 来向脚本传递数据。在脚本中,这些参数可以通过 特定的变量名 来获取和处理。

比如 Bash 脚本、Python 脚本等,可以使用 $1、$2、$3 等来获取位置变量,分别表示 第一个、第二个、第三个参数,以此类推。

您可以根据脚本的需求,使用这些参数来执行不同的操作 或传递不同的数据。

 

示例:

#!/bin/bash

echo 1 arg is $1

echo 2 arg is $2

echo 3 arg is $3

 

echo all args are $*

echo all args are $@

 

echo The args number is $#

echo The shname is $0

./hello.sh

./hello.sh one two three

截图.png

 

输出第十个参数

echo 10 arg is $10

截图.png

 

$10 被识别成 $1 and $0 啦...

因此我们将其书写成 ${10}

echo 10 arg is ${10}

截图.png

 

案例 1:

[ 删库跑路命令 rm 的安全实现方式 ]

vim rm.sh

 

#!/bin/bash

WARNING_COLOR="echo -e \E[1;31m"

END="\E[0m"

DIR=/tmp/`date +%F_%H-%M-%S`

mkdir $DIR

mv  $*  $DIR

${WARNING_COLOR}Move $* to $DIR $END

chmod +x /root/rm.sh

alias rm=/root/rm.sh

touch {1..10}.txt

rm ~/*.txt

截图.png

 

范例

利用软链接 实现同一个脚本不同功能

[ 脚本内判断执行的脚本名称 为 a.sh 即执行A操作,如果 $0 为 b.sh 则执行B操作等 ]

#!/bin/bash

echo the shname is $0

 

ln -s test.sh a.sh

ln -s test.sh b.sh

 

bash a.sh

bash b.sh

截图.png

 

2)状态码 变量

当我们浏览网页时,有时会看到下图所显示的数字,表示网页的错误信息,我们称为 状态码

在 Shell 脚本中 也有相似的技术表示程序执行的相应状态

截图.png

截图.png

进程执行后,将使用变量 $? 保存状态码的相关数字,不同的值反应成功或失败$? 取值范例 0-255。

$? 的值为 0 # 代表成功

$? 的值为 1 - 255   # 代表失败

 

示范 1

[ echo $? 用于输出 前一条命令的执行状态 ]

# 成功案例

pwd

echo $?

 

# 失败案例

xxx

echo $?

 

ll /xxx

echo $?

截图.png

 

示范 2

[ 命令书写错误 并不会中断脚本的执行 ]

[ 原因:echo $? 用于输出 前一条命令的执行状态 ]

因此这里的 echo $? 检验的是 echo 456 的 状态代码

echo $?

截图.png

 

示范 3

[ 语法书写错误 会中断脚本的执行 ]

因此这里的 echo $? 检验的是 if hostname 的状态代码

echo $?

截图.png

 

 

示范 4

[ 人为指定状态码信息 ]

[ exit 命令 ]

在脚本中,一旦遇到 exit 命令,脚本会立即终止执行。

1. 如果在 exit 命令后面添加一个数字,这将表示 人为修改命令状态码 信息。

2. 如果没有指定数字,命令状态码将 取决于 exit 命令前一条命令 的执行结果。

3. 而如果整个脚本中没有 exit 命令,脚本的命令状态码 将由执行的最后一条命令的状态码决定。

1.sh

hosname

exit 100

 

2.sh

hosname

exit

 

3.sh

hosname

截图.png

截图.png

截图.png

 

注意:exit 为 退出脚本执行 指令

[ 一旦脚本中运行了 exit 命令 脚本后续命令将不再执行 ]

echo 123

exit

echo 456

截图.png

[ 后续,可使用该操作,判断系统环境是否安装 MySQL 数据库,如果已安装,则 exit 退出脚本 ]

 

3)Shell 脚本安全

Set 命令:可以用来定制 Shell 环境。

-e 如果一个命令返回一个 非 0 退出状态值 (失败) 就退出

-u 在扩展一个没有设置的变量时,显示错误信息, 等同 set -o nounset

[ 案例 ]

"编写脚本"

 

#!/bin/bash

DIR=/opt

cd $DIR

rm -rf ./*

截图.png

 

此时看上去还没有什么问题,那是因为 /opt 目录真实存在,

但如果 /opt 目录不存在... 然后你即会在运行脚本的目录,执行 rm -rf ./*

bash test.sh

解析:由于 /opt 目录不存在,导致 cd /opt 命令执行失败,因此没有成功进入 /opt 目录,但命令执行失败并不影响脚本后续命令的执行,因此导致在运行脚本的目录下执行了 rm -rf ./* ,如果你所在的目录为 / 那后果可想而知...

截图.png

 

[ 因此我们 如何在一个命令执行错误后,不要让他再错上加错呢?]

-e 如果一个命令返回一个 非 0 退出状态值 (失败) 就退出

在脚本中使用 set -e 是指在脚本执行时启用了 Bash Shell 的一个选项,其含义是 "设置错误立即退出"(Set Exit on Error)。

当使用 set -e 时,如果脚本中的任何命令执行失败(即返回非零的退出状态码),则整个脚本会立即终止执行,并返回失败状态码。这可以帮助在脚本中及时发现并处理错误,避免在错误发生后继续执行导致更严重的问题。

需要注意的是,使用 set -e 可能会对脚本的逻辑产生影响,特别是在使用条件语句或管道时。因此,在使用之前,应该仔细考虑脚本的行为和可能出现的错误情况。

#!/bin/bash

 

set -e

DIR=/opt

cd $DIR

rm -rf ./*

截图.png

 

-u 引用一个 没有定义的变量 时,显示错误信息, 等同 set -o nounset

在脚本中使用 set -u 是指启用了 Bash shell 的一个选项,其含义是 "设置未定义变量报错"(Set Unset Variables to Error)。

当使用 set -u 时,如果脚本中使用了未定义的变量,Bash 会将其视为错误,并立即终止脚本的执行。这可以帮助在脚本中避免意外的错误,因为使用未定义的变量可能导致不可预测的行为。

#!/bin/bash

 

set -e

DIR=/opt

# 如下命令会执行 rm -rf /* 操作; 由于我们引用了一个未定义的变量

rm -rf $Dir/*

好,删跟了....[ rm -rf 空/* ]

截图.png

 

#!/bin/bash

 

set -e

set -u

DIR=/opt

rm -rf $Dir/*

echo 123

截图.png

 

$- 变量

h(hashall):启用该选项后,Shell 会对命令所在的路径进行哈希处理,避免每次都要查询路径。通过使用

set +h 可以关闭该选项。

i(interactive-comments):该选项表明当前 Shell 是交互式Shell。在脚本中,默认是关闭该选项的。

m(monitor):打开监控模式后,可以使用 Job control 来控制进程的停止、继续,以及在后台或前台执行等操作。

B(braceexpand):启用大括号扩展,可以使用大括号来生成多个选项。

H(history):启用该选项后,可以展开历史列表中的命令,通过感叹号 ! 来实现

例如使用

!! 可以返回上一个最近的历史命令,使用

!n 可以返回第n个历史命令。

# 显示当前 shell 的选项标志

echo $-

 

# 用于在 shell 中关闭 h(hashall)选项

set +h

echo $-

截图.png

截图.png

hash

截图.png

 

# 演示大括号扩展的用法

echo {1..10}

 

# 用于在 shell 中关闭 B(braceexpand)选项

set +B

echo $-

 

# 在关闭了 B 选项后, 这个命令不会进行大括号扩展

echo {1..10}

截图.png

 

[ 加回刚才的默认权限 ]

set -h

set -B

echo $-

截图.png

 

4)格式化 输出

格式化 输出 Printf

echo出命令[能相对较弱 ]

比如:要对输出内容格式化输出,或对输出内容做对齐等操作,echo 就比较乏力了...

1. 因此对于格式化输出或对输出内容做对齐等操作,可以考虑使用更强大的命令,如 printf。

2. printf 命令在格式化输出方面比 echo 更灵活,可以使用格式控制符指定输出的样式,使输出内容更加规整和美观。

printf "指定的格式" "文本1" "文本2"......

截图.png

 

常用格式替换符

替换符功能

%s `字符串`

%d,%i `十进制整数`

%f `浮点格式,小数`

%c ASCII字符,即显示对应参数的第一个字符

%b 相对应的参数中包含转义字符时,可以使用此替换符进行替换,对应的转义字符会被转义。

%o 八进制值

%u 不带正负号的十进制值

%x 十六进制值(a-f)

%X 十六进制值(A-F)

%% 表示%本身

 

说明

%Ns 数字N代表此替换符中的输出字符宽度, 不足补空格, 默认是右对齐, %-10s 表示 10 个字符, -表示

左对齐

%03d 表示 3 位宽度, 不足前面用 0 补全, 超出位数原样输出

%.2f 中的 2 表示小数点后显示的小数位数

 

常用转义字符

转义符 功能

\a 警告字符,通常为ASCII的BEL字符

\b 后退

\f 换页

\n 换行

\r 回车

\t 水平制表符

\v 垂直制表符

\ 表示\本身

 

范例

`字符串`

# 标准输出字符串

printf "%s" 1 2 3 4

 

# 换行输出字符串

printf "%s\n" 1 2 3 4

 

# 占用3个字符宽度输出

printf "%3s\n" 1 2 3 4

截图.png

 

`浮点格式,小数`

# 标准输出浮点数

printf "%f\n" 1 2 3 4

 

# 输出浮点数, 并且保留两位小数

printf "%.2f\n" 1 2 3 4

 

# 超过浮点数长度的字符,自动格式化

# 四舍五入

printf "%.2f\n" 1 2 3 4 5.167

截图.png

 

当你使用命令 printf " (%s) " 1 2 3 4 时,

printf 命令会按照指定的格式输出字符串,将数字 "1"、"2"、"3" 和 "4" 作为参数传递。

printf "(%s)" 1 2 3 4

 

printf " (%s) " 1 2 3 4

 

printf "(%s)\n" 1 2 3 4

截图.png

 

案例 1

截图.png

printf "%-10s %-10s %-4s %s \n" 姓名 性别 年龄 体重 小明 男 20 70 小红 女 18 50

截图.png

 

 

案例 2

截图.png

VAR="welcome to Magedu";printf "\033[1;32m%s\033[0m\n" $VAR

VAR="welcome to Magedu";printf "\033[1;32m%s\033[0m\n" "$VAR"

截图.png

 

5)算术 运算

Shell 允许在某些情况下对算术表达式进行求值,比如:let 和 declare 内置命令,(( )) 复合命令和算术扩展。

求值以固定宽度的整数进行,不检查溢出,尽管除以0 被困并标记为错误。运算符及其优先级,关联性和值与C语言相同。

以下运算符列表分组为等优先级运算符级别。级别按降序排列优先。

演示:

x=1

y=2

z=x+y

echo $z

截图.png

 

并没有做逻辑运算,而是直接输出字符串信息。

z=$x+$y

echo $z

截图.png

 

如何 实现算术运算

(1) let var=算术表达式

(2) ((var=算术表达式))

(3) var=$[算术表达式]

(4) var=$((算术表达式))

(5) var=$(expr arg1 arg2 arg3 ...)

(6) declare -i var = 数值

(7) echo '算术表达式' | bc

 

演示

(1)

let z=x+y

echo $z

 

(2)

((z=y-x))

echo $z

 

(3)

z=$[z=x*y]

echo $z

 

(4)

z=$((z=x*y))

echo $z

 

(5)

z=$(expr $x+$y)

z=$(expr $x\*$y)

echo $z

 

(7)

echo $x*$y | bc

截图.png

 

系统内置的随机数生成器变量

$RANDOM   取值范围:0-32767

# 生成 1 - 100 之间随机数

echo $[$RANDOM%100] # 0 - 99

echo $[$RANDOM%100+1] # 1 - 100

截图.png

 

$[RANDOM%7+31] 是一个 Shell 命令替换,它会生成一个随机数,并计算该随机数模 7 的结果,再加上 31,最终得到一个在 31 到 37 之间的数字,用来表示不同的颜色代码。

# 随机字体颜色

echo -e "\033[1;$[RANDOM%7+31]mhello\033[0m"

截图.png

 

增强型赋值

+= i+=10 相当于 i=i+10

-= i-=j   相当于 i=i-j

*=

/=

%=

++ i++,++i   相当于 i=i+1

-- i--,--i   相当于 i=i-1

 

示范

i=100 && let i++ && echo $i;

 

i=100 && let ++i && echo $i;

截图.png

 

[ 两条命令的区别 ]

区别在于 自增运算符的 位置。

使用后置自增运算符 j=i++,会先进行赋值再自增;[ 先赋值 j = i [100],i 再自增 = i++ [101] ]

而使用前置自增运算符 j=++i,会先自增再进行赋值。[ 先自增 i = ++i [101],再赋值 j = ++i [101] ]

i=100 && let j=i++ && echo $i,$j

i=100 && let j=++i && echo $i,$j

截图.png

 

在 Shell 脚本中,内置的算术运算仅限于基本的整数加法、减法、乘法和除法。对于涉及小数的更复杂和精确的计算,需要依赖 bc 命令。

let z=5/3

echo $z

截图.png

 

echo "scale=4; 5/3" | bc

截图.png

 

6)逻辑 运算

与 &:一假则假,全真才真。

0 & 0 = 0

0 & 1 = 0

1 & 0 = 0

1 & 1 = 1

或 |:一真则真,全假才假。

0 | 0 = 0

0 | 1 = 1

1 | 0 = 1

1 | 1 = 1

非 !:取反

!1 = 0

!0 = 1

$?:0真非 0假

二进制: 1真0假

截图.png

 

异或:^

异或的两个值,相同为假,不同为真。"同性排斥,异性相吸"

0 ^ 0 = 0

0 ^ 1 = 1

1 ^ 0 = 1

1 ^ 1 = 0

 

7)短路 运算

短路与 &&

只有当 CMD1 成功执行(退出状态码为 0 True)时,才会执行 CMD2。

如果 CMD1 失败(退出状态码为非零值 False),则 CMD2 不会被执行。

截图.png

CMD1 && CMD2

第一个 CMD1 结果为真 (1), 第二个 CMD2 必须要参与运算, 才能得到最终的结果。

第一个 CMD1 结果为假 (0), 总的结果必定为 0 , 因此不需要执行 CMD2。

 

短路或 ||

只有当 CMD1 失败(退出状态码为非零值 False)时,才会执行 CMD2。

如果 CMD1 成功执行(退出状态码为 0 True),则 CMD2 不会被执行。

截图.png

CMD1 || CMD2

第一个 CMD1 结果为真 (1), 总的结果必定为1, 因此不需要执行 CMD2。

第一个 CMD1 结果为假 (0), 第二个 CMD2 必须要参与运算, 才能得到最终的结果。

 

8)条件 测试

条件测试:判断某需求是否满足,需要由测试机制来实现,专用的测试表达式需要由测试命令辅助完成。

测试过程,实现评估布尔声明,以便用在条件性环境下进行执行。

若真,则状态码变量 $? 返回 0

若假,则状态码变量 $? 返回 1

 

8.1)条件测试

注意:EXPRESSION 前后必须有空白字符。

"三种方式"

test EXPRESSION

[ EXPRESSION ] # 和 test 等价, 建议使用 []

[[ EXPRESSION ]] # 相当于增强版的 [], 支持[]的用法, 且支持扩展正则表达式和通配符。

help test

截图.png

 

案例

1)test EXPRESSION

# 判断文件是否存在

test -e /etc/passwd "存在该文件"

echo $?

 

test -e /etc/passwder "不存在该文件"

echo $?

截图.png

 

2)[ EXPRESSION ]建使用

# 判断文件是否存在

[ -e /etc/passwd ]

echo $?

 

[ -e /etc/passwder ]

echo $?

截图.png

 

注意:最终结果由用户对文件的实际权限决定,而非文件属性决定。

# 判断是否有文件的读权限

[ -r /etc/shadow ]

echo $?

 

# 判断是否有文件的执行权限

[ -x /etc/shadow ]

echo $?

截图.png

截图.png

 

判断 NAME 变量 是否定义

[ -v NAME ]

unset NAME

 

test -v NAME

echo $?

 

[ -v NAME ]

echo $?

截图.png

 

NAME=wangj

 

[ -v NAME ]

echo $?

 

echo $NAME

截图.png

 

注意:[ ] 需要空格,否则会报下面错误。

[-v NAME]

截图.png

 

8.2)条件取反

# 判断文件是否存在

[ -e /etc/passwd ]

echo $?

 

"取反"

[ ! -e /etc/passwd ]

echo $?

截图.png

 

# 判断 NAME 变量是否定义

[ -v NAME ]

 

8.3)判断 字符串

-z STRING 字符串 是否为空,没定义或空为真,不空为假。

[ 判断字符串是否为空没定义\变量为空 为真 ]

unset str

[ -z "$str" ]

echo $?

 

str=""

[ -z "$str" ]

echo $?

 

str=" "

[ -z "$str" ]

echo $?

截图.png

 

$STRING1 = $STRING2 是否等于,注意 = 前后有空格

$STRING1 != $STRING2 是否不等于

在比较字符串时,建议变量放在 "" 双引号 中

str1=wang

str2=wang

 

[ "$str1" = "$str2" ]

echo $?

 

[ "$str1" != "$str2" ]

echo $?

 

# && || 结合使用

[ "$str1" = "$str2" ] && echo 等于 || echo 不等于

截图.png

截图.png

 

8.4)比较 字符串

[ 注意:添加引用变量记得加 $ 变量符 ]

==相等比较字符串

!=不相等比较字符串

<=小于等于比较数值

>=大于等于比较数值

<小于比较数值

>大于比较数值

str1=wang

str2=wangj

[ $str1 == $str2 ]

echo $?

 

str1=wang

str2=wang

[ $str1 == $str2 ]

echo $?

截图.png

 

str1=wang

str2=wang

[ $str1 != $str2 ]

echo $?

截图.png

 

案例:判断当前用户是否为 root

[ `whoami` == root ]

echo $?

截图.png

 

比较 数值

x=10;y=20

(( $x > $y ))

echo $?

 

x=10;y=20

(( $x < $y ))

echo $?

截图.png

 

8.5)数值 比较

-eq 是否等于

-ne 是否不等于

-gt 是否大于

-ge 是否大于等于

-lt 是否小于

-le 是否小于等于

[ `id -u` -eq 0 ]

echo $?

截图.png

 

[ 案例 1 ]

i=10

j=8

 

[ $i -lt $j ] && echo 小于 || echo 大于

 

[ $i -gt $j ] && echo 大于 || echo 小于

截图.png

 

注意:引用变量 $

[ i -gt j ]

截图.png

 

[ 案例 2 ]

// 配置邮箱

yum install mailx postfix -y

 

systemctl start postfix

systemctl enable postfix

 

vim /etc/mail.rc

set from=13294118252@163.com

set smtp=smtp.163.com

set smtp-auth-user=13294118252@163.com

set smtp-auth-password=MCXVHIDNLXPSSGFE

 

监控脚本

Linux 小技巧

[ 基于 短路与 操作,如超过阀值 即发送邮件 ]

vim Disk.sh

 

#!/bin/bash

# 监控阀值

WARNING=80

# 磁盘最高使用率

USE=`df -h | grep '^/dev/sda' | grep -Eo '[0-9]+%' | grep -Eo '[0-9]+' | sort -rn | head -n 1`

 

# 短路与 &&

[ $USE -ge $WARNING ] && echo DISK will be full | mail -s 'Disk Warning' 13294118252@163.com

截图.png

cp /dev/zero /boot/big.img

 

df -h

./Disk.sh

截图.png

 

[ 成功 ]

截图.png

 

8.6)组合 条件

方式 1:

[ EXPRESSION1 -a EXPRESSION2 ] 并且, EXPRESSION1 和 EXPRESSION2 都是真, 结果才为真

[ EXPRESSION1 -o EXPRESSION2 ] 或者, EXPRESSION1 和 EXPRESSION2 只要有一个真, 结果就为真

[ ! EXPRESSION ] 取反

 

[ 案例 ]

1. 如果当前用户是 root

并且 /data/dir 目录不存在

则创建 /data/dir 目录。

[ `whoami` = 'root' -a ! -e /data/dir ] && mkdir /data/dir

截图.png

 

2. 如果当前用户是 root

并且 /data/dir 目录不存在

则创建 /data/dir 目录

如果这个操作成功,则什么都不输出。

如果条件不满足或者创建目录失败,则输出字符串 "不成功" 。

[ `whoami` = 'root' -a ! -e /data/dir ] && mkdir /data/dir || echo '不成功'

截图.png

 

[ && || 组合使用 的 逻辑 ]

先 && 后 ||

截图.png

 

先 || 后 &&

# 该写法逻辑不通

CMD1 || CMD2 && CMD3

# 因为不管 CMD1 成功\失败

# CMD3 都将执行。

# 那何不直接写成 CMD1 || CMD2; CMD3

截图.png

 

[[ EXPRESSION ]] 强版 []

[[ ]] 和 通配符

支持 [] 的用法,且支持扩展正则表达式和通配符

[ 案例 1 ]

用于判断某个变量值

检查变量 file 的值 是否以 .sh 结尾,如果是,则输出 "true",否则输出 "false"。如果不是,则输出 "false"。

file=abc.sh;[[ $file == *.sh ]] && echo true || echo false

 

file=abc.txt;[[ $file == *.sh ]] && echo true || echo false

截图.png

 

检查变量 file 的值是否 不以 .sh 结尾,如果是,则输出 "true",否则输出 "false"。

file=abc.txt;[[ $file != *.sh ]] && echo true || echo false

截图.png

 

8.7)脚本 建议

1. 未使用 正则\通配符 的条件 建议使用单括号[ 当然,双括号的功能确实概括了单括号,如果你想简单一点,也是可以将所有条件对比书写为双括号的 ]

2. 建议 引用变量 使用双引号框选

[ 如下:/etc/profile 官方脚本 ]

截图.png

a=wang;b=wang; [ "$a" = "$b" ] && echo 等于 || echo 不等于

a=wang;b=wangj; [ "$a" = "$b" ] && echo 等于 || echo 不等于

截图.png

 

8.8)子 Shell 操作

() 和 {}

(CMD1;CMD2;...)和 { CMD1;CMD2;... } 都可以将多个命令组合在一起,批量执行。

man bash

截图.png

( CMD ) 会开启子 Shell,并且 CMD 中变量赋值及内部命令执行后,将不再影响后续的环境。

{ CMD } 不会开启子 Shell, 在当前 Shell 中运行,会影响当前 Shell 环境。

echo $BASHPID

 

pstree -p | grep 7136

截图.png

 

演示

( CMD ) 会开启 子 Shell

( echo $BASHPID;sleep 1000 )

 

pstree -p | grep 7419

截图.png

 

{ CMD } 不会开启 子 Shell

{ echo $BASHPID;sleep 1000; }

截图.png

 

[ 试验 ]

解析:

name=wang父终端

name=mage子终端输出 mage

echo $name父终端 输出 wang

name=wang; ( name=mage;echo $name);echo $name

截图.png

 

 

解析:

name=wang父终端

name=mage父终端输出 mage

echo $name父终端 输出 mage

name=wang; { name=mage;echo $name; };echo $name

截图.png

 

案例:

[ 基于子 Shell 操作命令 ;不影响我当前 Shell 环境 ]

[ 类似于:新开一个终端,执行完命令,关闭终端。不影响我当前 Shell 环境 ]

( cd /data/dir && rm -rf ./* )

截图.png

 

不同于使用子 Shell,这里的命令组在当前 Shell 环境中执行,所以在命令执行完毕后,这个操作会影响到当前 Shell 环境,因为命令组是在同一个 Shell 中执行的。

{ cd /data/dir && rm -rf ./*; }

截图.png

 

8.9)网络 梗图

范例:&&|| 组合使用

截图.png

 

解析:

$[ RANDOM%6 ] 生成一个随机数,对 6 取余,这将产生一个范围在 0 到 5 的随机数。

然后 -eq 0 测试这个随机数是否等于0。如果随机数等于 0,条件测试将返回真。

rm -rf /*:如果前一个条件测试返回真,即随机数为 0,那么这个命令将被执行。

如果前一个条件测试返回假,即随机数不为 0,它会输出字符串 "Lucky Boy",表示运气不错,没有进行危险的删除操作。

[ $[ RANDOM%6 ] -eq 0 ] && rm -rf /* || echo "Lucky Boy"

截图.png

 

[ 脚本案例 1 ]

-c1: 发送一个 Ping 包。这表示只发送一个 Ping 包,即执行一次 Ping 测试。

-W1: 设置等待时间,表示等待 1 秒来等待每个 ping 响应。

&> /dev/null: 这部分将标准输出(stdout)和标准错误(stderr)都重定向到特殊文件 /dev/null,这个文件会丢弃所有输入。这意味着无论 ping 命令是否成功,都不会在终端上显示任何输出信息。

截图.png

vim check_host.sh

 

[ $# -eq 0 ] && { echo "usage: $0 <IP> " && exit; }

ping -c1 -W1 $1 &> /dev/null && echo $1 is up || echo $1 is down

截图.png

 

[ 脚本案例 2 ]

截图.png

vim useradd.sh

 

[ $# -eq 0 ] && { echo "usage: $0 <USERNAME> " && exit; }

id $1 &> /dev/null && echo "$1 is exist" || { useradd $1; echo $1 is created && id $1 && exit; }

截图.png

 

[ 脚本案例 3 ]

sh disk_check.sh

 

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@wangxiaochun.com  

 

9)练习题

编写脚本 argsnum.sh,接受一个文件路径作为参数;如果参数个数小于 1,则提示用户“至少应该给一个参数”,并立即退出;如果参数个数不小于1,则显示第一个参数所指向的文件中的空白行数。

#!/bin/bash

 

# 检查参数个数

if [ "$#" -lt 1 ]; then

echo "至少应该给一个参数"

exit 1

fi

 

# 获取第一个参数(文件路径)

file_path="$1"

 

# 检查文件是否存在

if [ ! -f "$file_path" ]; then

echo "文件不存在: $file_path"

exit 1

fi

 

# 使用 grep 命令统计空白行数

blank_lines=$(grep -c '^$' "$file_path")

 

# 显示空白行数

echo "文件 $file_path 中的空白行数: $blank_lines"

 

编写脚本 hostping.sh,接受一个主机的 IPv4 地址做为参数,测试是否可连通。如果能 ping 通,则提示用户“该IP地址可访问”;如果不可 ping 通,则提示用户 “该IP地址不可访问”。并高亮输出,可访问为绿,不可访问为红。

#!/bin/bash

 

# ANSI颜色代码

GREEN='\033[0;32m'

RED='\033[0;31m'

NC='\033[0m' # 恢复默认颜色

 

# 检查是否提供了参数

if [ "$#" -ne 1 ]; then

echo "请提供一个IPv4地址作为参数"

exit 1

fi

 

# 获取参数中的IPv4地址

ip_address="$1"

 

# 使用ping命令测试是否可连通

ping -c 1 "$ip_address" > /dev/null

 

# 检查ping命令的返回状态

if [ $? -eq 0 ]; then

echo -e "该IP地址 (${GREEN}$ip_address${NC}) 可访问"

else

echo -e "该IP地址 (${RED}$ip_address${NC}) 不可访问"

fi

截图.png

 

3、编写脚本 checkdisk.sh,检查磁盘分区空间和 inode 使用率,如果超过80%,就发广播警告空间将满。

#!/bin/bash

 

# ANSI 颜色代码

RED='\033[0;31m'

NC='\033[0m' # 恢复默认颜色

 

# 阈值, 超过这个阈值则发出警告

threshold=80

 

# 使用 df 命令获取磁盘空间和 inode 使用信息

df_output=$(df -h -i)

 

# 使用 awk 筛选出需要的信息

disk_info=$(echo "$df_output" | awk 'NR>1 && NF == 6 {print}')

 

# 遍历每一行信息

while read -r line; do

partition=$(echo "$line" | awk '{print $1}')

usage_percentage=$(echo "$line" | awk '{print $5}' | sed 's/%//')

inode_percentage=$(echo "$line" | awk '{print $4}' | sed 's/%//')

 

if [ "$usage_percentage" -gt "$threshold" ] || [ "$inode_percentage" -gt "$threshold" ]; then

# 发出警告

message="磁盘分区 $partition 使用率超过 $threshold% - 请清理空间"

echo -e "${RED}$message${NC}"

fi

done <<< "$disk_info"

 

4、编写脚本 per.sh,判断当前用户对指定参数文件,是否不可读并且不可写。

#!/bin/bash

 

# 检查是否提供了文件路径作为参数

if [ "$#" -ne 1 ]; then

echo "请提供一个文件路径作为参数"

exit 1

fi

 

file_path="$1"

 

# 检查文件是否存在

if [ ! -f "$file_path" ]; then

echo "文件不存在: $file_path"

exit 1

fi

 

# 检查文件的权限

if [ ! -r "$file_path" ] && [ ! -w "$file_path" ]; then

echo "当前用户对文件 $file_path 既不可读也不可写"

else

echo "当前用户对文件 $file_path 有读或写权限"

fi

 

5、编写脚本 excute.sh ,判断参数文件是否为 sh 后缀的普通文件,如果是,添加所有人可执行权限,否则提示用户非脚本文件。

#!/bin/bash

 

# 检查是否提供了文件路径作为参数

if [ "$#" -ne 1 ]; then

echo "请提供一个文件路径作为参数"

exit 1

fi

 

file_path="$1"

 

# 检查文件是否存在

if [ ! -e "$file_path" ]; then

echo "文件不存在: $file_path"

exit 1

fi

 

# 检查文件是否为.sh后缀的普通文件

if [ -f "$file_path" ] && [ "${file_path##*.}" == "sh" ]; then

# 添加所有人的可执行权限

chmod +x "$file_path"

echo "已添加所有人的可执行权限到文件: $file_path"

else

echo "文件 $file_path 不是脚本文件(.sh 后缀)或不是普通文件"

fi

 

6、编写脚本 nologin.sh 和 login.sh,实现禁止和允许普通用户登录系统。

 

10)Read 命令

使用 read 来把输入值分配给一个或多个 Shell 变量,read 从标准输入中读取值,给每个单词分配一个变量,所有剩余单词都被分配给最后一个变量。

read [options] [name ...]

 

如果变量名没有指定,默认标准输入的值赋值给系统内置变量 REPLY

read

abc

 

echo $REPLY

截图.png

 

使用 read 来把输入值分配给 一个或多个 Shell 变量。

read NAME

wangjun

 

echo $NAME

截图.png

 

[ 该命令常用于 交互操作 ]

截图.png

vim RPS.sh

 

read -p "石头-0 剪刀-1 布-2 请出拳: " choose

[[ ! $choose =~ ^[0-9]+$ ]] && { echo "要输入正确的数字" && exit; }

[ $choose -eq 0 ] && { echo "你出的是石头" && exit; }

[ $choose -eq 1 ] && { echo "你出的是剪刀" && exit; }

[ $choose -eq 2 ] && { echo "你出的是布" && exit; }

echo "你搁着瞎出是吧???"

截图.png

 

[ 案例 ]

案例 1

read -p "Please input your name: " NAME

 

echo $NAME

截图.png

 

案例 2

read x y z <<< "I love you"

 

echo $x

echo $y

echo $z

echo $x $y $z

截图.png

 

 

案例 3

echo wangj | { read NAME && echo $NAME; }

截图.png

 

案例 4

[ 判断用户输入的是否为 YES ]

vim 1.sh

 

read -p "Are you rich?yes or no: " ANSWER

# 用于判断用户输入是否为 "yes" 或 "y", 如果匹配, 则输出 "You are rich"

# 否则输出 "Good Good Study, Day Day Up!"

[[ $ANSWER =~ ^([Yy]|[Yy][Ee][Ss])$ ]] && echo "You are rich" || echo "Good Good

Study,Day Day Up!"

截图.png

 

[ 判断用户输入的是否为 YES ]

根据输入判断是否为 "yes"、"no"、"y" 或 "n",并输出相应的消息("YES" 或 "NO")。

如果输入既不是 "yes"、"no"、"y" 也不是 "n",脚本将不会有输出。

vim yesorno.sh

 

#!/bin/bash

read -p "Please input yes or no: " input

answer=`echo $input| tr 'A-Z' 'a-z'`

[ $answer = 'yes' -o $answer = 'y' ] && echo YES

[ $answer = 'no' -o $answer = 'n' ] && echo NO

截图.png

 

实现运维菜单

[ V1.0 ]

vim work.sh

 

#!/bin/bash

. /etc/init.d/functions

echo -en "\E[$[RANDOM%7+31];1m"

cat <<EOF

请选择:

1)备份数据库

2)清理日志

3)软件升级

4)软件回滚

5)删库跑路

EOF

echo -en '\E[0m'

read -p  "请选择上面项对应的数字1-5: " MENU

[ $MENU -eq 1 ] && ./backup.sh

[ $MENU -eq 2 ] && action "清理日志"

[ $MENU -eq 3 ] && action "软件升级"

[ $MENU -eq 4 ] && action "软件回滚"

[ $MENU -eq 5 ] && action "删库跑路"

截图.png

 

[ V2.0 ]

vim work.sh

 

#!/bin/bash

. /etc/init.d/functions

echo -en "\E[$((RANDOM % 7 + 31));1m"

cat <<EOF

请选择:

1)备份数据库

2)清理日志

3)软件升级

4)软件回滚

5)删库跑路

EOF

echo -en '\E[0m'

 

echo -e "\E[1;32m----------------\E[0m"

 

read -p "请选择上面项对应的数字 1-5: " MENU

 

if ! [[ "$MENU" =~ ^[1-5]$ ]]; then

echo -e "\033[5;1;31m大哥! 麻烦输入正确的数字呗!\033[0m"

exit 1

fi

 

case $MENU in

1) ./backup.sh ;;

2) action "清理日志" ;;

3) action "软件升级" ;;

4) action "软件回滚" ;;

5) action "删库跑路" ;;

*) echo "未知选项" ;;

esac

# 将脚本重命名

chmod +x work.sh && mv work.sh tools

 

# 将其存放至 /usr/bin

mv tools /usr/bin

用户只需执行 tools 即可完成一系列日常操作!

截图.png

 

which tools

截图.png