目录

1 shell 脚本语言的基本用法

1.1 shell 脚本注释规范

1.1.1 shell 脚本注释规范

1.1.2 执行(5种)

1.1.3 在远程主机运行本地脚本

1.1.4 检查shell脚本

1.2 shell变量

1.2.1 Shell中变量命名法则

1.2.2 变量赋值与引用

1.2.3 环境变量的查看设置和删除

1.2.4 只读变量

1.2.5 位置变量

1.2.6 退出状态码变量

1.2.7 展开命令行

1.3 退出状态码变量

1.4 展开命令行

1.5 脚本安全和 set

1.6 格式化输出 printf命令

1.7 echo 命令

1.8 算术运算

1.9 短路运算

1.10 条件测试命令

1.10.1 条件测试命令

1.10.2 常用选项:文件判断相关

1.10.3 常用选项:字符串判断相关

1.10.4 常用选项:数学相关

1.10.5 变量测试

1.10.6 字符串测试

1.11 关于 () 和 {}

1.12 组合测试条件

2 bash shell 的配置文件

2.1 shell登录两种方式分类

2.2 按功能划分分类

2.3 编辑配置文件生效

2.4 Bash 退出任务

3 流程控制

3.1 选择执行 if 语句

3.2 条件判断 case 语句

3.3 循环的命令 for命令

3.3.1 方法 一

3.3.2 方法二

3.4 循环 while

3.4.1 无限循环

3.4.2 while 特殊用法 while read

3.4.3 循环 until(反的while)

3.5 循环与菜单 select

3.6 控制语句

3.6.1 循环控制语句 continue

3.6.2 循环控制语句 break

3.6.3 循环控制 shift 命令

4 函数 function

4.1 函数介绍

4.2.1 定义函数

4.2.2 查看函数

4.2.3 删除函数

4.3 函数调用

4.3.2 在脚本中定义及使用函数

4.4 函数返回值

4.5 环境函数

4.6 函数递归

5 其它脚本相关工具

5.1 信号捕捉 trap

5.2 创建临时文件 mktemp

6 数组 array

6.3 数组赋值

6.4 显示所有数组

6.5 引用数组

6.6 删除数组

6.8 关联数组

7 字符串处理

7.1 字符串切片

shell 脚本案例

1 显示指标(括CPU型号,内存大小,硬盘大小,操作系统版本)

2.编写脚本 backup.sh,可实现每日将 /etc/ 目录备份到 /backup/etcYYYY-mm-dd中

3.编写脚本 disk.sh,显示当前硬盘分区中空间利用率最大的值

4.编写脚本 links.sh,显示正连接本主机的每个远程主机的IPv4地址和连接数,并按连接数从大到小 排 序


1 shell 脚本语言的基本用法

一个shell脚本文件中主要包含以下内容

  • 各种系统命令的组合

  • 数据存储:变量、数组

  • 表达式:a + b

  • 控制语句:if

格式要求:首行shebang机制(#!)

#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
#!/usr/bin/ruby
#!/usr/bin/lua

有的时候被启用有的时候不被启用

1.1 shell 脚本注释规范

一般要注明作者,创建(修改)时间,文件名,版本号,该程序的功能及作用,目地,版权信息,作者联系 方式等

1.1.1 shell 脚本注释规范

[root@bogon test.dir]# cat ~/.vimar
cat: /root/.vimar: No such file or directory
[root@bogon test.dir]# cat ~/.vimrc
set nu
set ts=4
autocmd BufNewFile *.sh exec ":call SetTitle()"
func SetTitle()if expand("%:e") == 'sh'call setline(1,"#!/bin/bash")call setline(2,"#")call setline(3,"#****************************************************")call setline(4,"#Author:           caojidong")call setline(5,"#QQ:               1549396190")call setline(6,"#Date:             ".strftime("%Y-%m-%d"))call setline(7,"#FileName:         ".expand("%"))call setline(8,"#cell-phone number:               13739548267")call setline(9,"#Description:      test")call setline(10,"#Copyright(C):     ".strftime("%Y")." All right")call setline(11,"#***************************************************")
​endif
endfunc
​

1.1.2 执行(5种)

[root@bogon test.dir]# ./1.test.sh  #./sh,就是会去使用脚本中声明的第一行的解释器来解释脚本的内容
hello word
caojidong
[root@bogon test.dir]# . 1.test.sh
hello word                         #在当前进程中执行
caojidong
[root@bogon test.dir]# bash 1.test.sh
hello word
caojidong
[root@bogon test.dir]# sh 1.test.sh  #使用sh install.sh 如果这个脚本文件当前用户没有执行的权限也是能够执行的
hello word
caojidong
[root@bogon test.dir]# /tmp/test.dir/1.test.sh
hello word
caojidong
​
借鉴别人写的挺好的文章:
https://blog.csdn.net/weixin_37569048/article/details/97764626?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_title~default-1.no_search_link&spm=1001.2101.3001.4242
————————————————
版权声明:本文为CSDN博主「如梦@_@」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43726471/article/details/120800757

瞬间启动子shell :用括号表示。

echo $BASHPID;( echo $BASHPID; sleep 300; )
​

1.1.3 在远程主机运行本地脚本

本地执行远程脚本

[root@rocky86 ~]# curl http://10.0.0.157/test.sh -s | bash
hello, world
Hello, world!
[root@rocky86 ~]# curl http://10.0.0.157/test.sh 2>/dev/null | bash
hello, world
Hello, world!
[root@rocky86 ~]# wget -qO - http://10.0.0.157/test.sh | bash
hello, world
Hello, world!

curl和wget的区别和使用

1.下载文件

curl -O http://man.linuxde.net/text.iso                    #O大写,不用O只是打印内容不会下载
wget http://www.linuxde.net/text.iso                       #不用参数,直接下载文件

2.下载文件并重命名

curl -o rename.iso http://man.linuxde.net/text.iso         #o小写
wget -O rename.zip http://www.linuxde.net/text.iso         #O大写

3.断点续传

curl -O -C -URL http://man.linuxde.net/text.iso            #C大写
wget -c http://www.linuxde.net/text.iso                    #c小写

4.限速下载

curl --limit-rate 50k -O http://man.linuxde.net/text.iso
wget --limit-rate=50k http://www.linuxde.net/text.iso

5.显示响应头部信息

curl -I http://man.linuxde.net/text.iso
wget --server-response http://www.linuxde.net/test.iso

6.wget利器--打包下载网站

wget --mirror -p --convert-links -P /var/www/html http://man.linuxde.net/

在远程主机运行(本地脚本)

[root@rocky86 ~]# cat test2.sh
#!/bin/bash
#
#****************************************************
#Author: jose
#QQ: 123456
#Date: 2022-08-16
#FileName: test2.sh
#URL: http://www.magedu.com
#Description: test
#Copyright(C): 2022 All right
#***************************************************
hostname -I
#本地执行
[root@rocky86 ~]# bash test2.sh
10.0.0.150
#远程主机上执行
[root@rocky86 ~]# ssh root@10.0.0.157 /bin/bash < test2.sh
root@10.0.0.157's password:
10.0.0.157

1.1.4 检查shell脚本

  • 语法错误:会导致后续的命令不继续执行,可以用 bash -n 检查错误,提示的出错行数不一定是准 确的

  • 命令错误:默认后续的命令还会继续执行,用 bash -n 无法检查出来 ,可以使用 bash -x 进行观察

  • 逻辑错误:只能使用 bash -x 进行观察

此选项只能检测脚本中的语法错误,不能检测命令错误,也不会执行脚本

bash -n test.sh #bash -n 脚本名称
​

逐行输出命令,并输出执行结果

bash -x test.sh  #bash -x 脚本名称

1.2 shell变量

变量数据类型:

字符
数值:整型、浮点型,bash 不支持浮点数
布尔
指针
结构体

静态和动态语言

静态编译语言:使用变量前,先声明变量类型,之后类型不能改变,在编译时检查,如:java,c
​
动态编译语言:不用事先声明,可随时改变类型,如:bash,Python

强类型和弱类型语言

强类型语言:不同类型数据操作,必须经过强制转换才同一类型才能运算,如: java , c# ,
python
弱类型语言:语言的运行时会隐式做数据类型转换。无须指定类型,默认均为字符型;参与运算会
自动进行隐式类型转换;变量无须事先定义可直接调用,如:bash ,php,javascript
​

1.2.1 Shell中变量命名法则

命名要求

  • 区分大小写

  • 不能使程序中的保留字和内置变量:如:if, for

  • 只能使用数字、字母及下划线,且不能以数字开头,注意:不支持短横线 “ - ”,和主机名相反

#主机名只允许含ascii字符里的数字和字母,-和. 。其余的都不允许。

运行shell时,会同时存在三种变量:

变量的生效范围等标准划分变量类型

  1、局部变量:局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。

【小写:局部变量小写 函数名小写

大驼峰StudentFirstName,由多个单词组成,且每个单词的首字母是大写,其它小写

小驼峰studentFirstName ,由多个单词组成,第一个单词的首字母小写,后续每个单词的首字母 是 大写,其它小写】

  2、环境变量:所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行,必要的时候shell脚本也可以定义环境变量。

【大写】

  3、shell变量:shell变量是由shell程序设置的特殊变量,shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行。

1.2.2 变量赋值与引用

1.2.2.1 赋值

value 可以是以下多种形式

name='root' #直接字串
name="$USER" #变量引用
name=`COMMAND` #命令引用
name=$(COMMAND) #命令引用

注意:变量赋值是临时生效,当退出终端后,变量会自动删除,无法持久保存,脚本中的变量会随着脚 本结束,也会自动删除

#变量保存、变量永久保存方法、变量永久保存路径

在/etc/profile文件中添加变量【对所有用户生效(永久的)】
[root@bogon ~]# vim /etc/profile
注:修改文件后要想马上生效还要运行$ source /home/guok/.bash_profile不然只能在下次重进此用户时生效。
​

1.2.2.2 引用

$name
${name}

弱引用和强引用 "$name" 弱引用,其中的变量引用会被替换为变量值 '$name' 强引用,其中的变量引用不会被替换为变量值,而保持原字符串

变量的间接赋值和引用(变量和变量引用)

[root@centos8 ~]#TITLE=cto
[root@centos8 ~]#NAME=wang
[root@centos8 ~]#TITLE=$NAME
[root@centos8 ~]#echo $NAME
wang
[root@centos8 ~]#echo $TITLE
wang
[root@centos8 ~]#NAME=mage
[root@centos8 ~]#echo $NAME
mage
[root@centos8 ~]#echo $TITLE
wang
​

变量追加值

[root@centos8 ~]#TITLE=CTO
[root@centos8 ~]#TITLE+=:wang
[root@centos8 ~]#echo $TITLE
CTO:wang

利用变量实现动态命令

[root@centos8 ~]#CMD=hostname
[root@centos8 ~]#$CMD
centos8.magedu.com
​

1.2.3 环境变量的查看设置和删除

定义bash环境的用户文件是:.bashrc &bash_profile

使用echo命令查看单个环境变量。例如:

root@bogon ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin

使用env查看所有环境变量。例如:

[root@bogon ~]# env

查看指定进程的环境变量

cat /proc/$PID/environ #新指定的不输出

export命令定义变量

使用set查看所有本地定义的环境变量。

unset可以删除指定的环境变量。

[root@bogon test.dir]# export mage="66666666handsome"
[root@bogon test.dir]# set | grep mage
_=mage
mage=66666666handsome
[root@bogon test.dir]# unset mage
[root@bogon test.dir]# echo "$mage"

变量赋值:

#声明并赋值
export name=VALUE
declare -x name=VALUE
#或者分两步实现
name=VALUE
export name
​

declare 声明变量用法:

declare -x 声明环境变量export
declare -r 声明只读变量readonly
declare -i 声明整数int

bash内建的环境变量

PATH
SHELL
USER
UID
HOME
PWD
SHLVL #shell的嵌套层数,即深度
LANG
MAIL
HOSTNAME
HISTSIZE
_   #下划线,表示前一命令的最后一个参数
​

1.2.4 只读变量

只读变量:只能声明定义,但后续不能修改和删除,即常量 声明只读变量:

readonly name
declare -r name
​
readonly [-p]
declare -r
​
[root@centos8 ~]#readonly PI=3.14159
[root@centos8 ~]#echo $PI
3.14159
[root@centos8 ~]#PI=3.14
-bash: PI: readonly variable
[root@centos8 ~]#unset PI
-bash: unset: PI: cannot unset: readonly variable
[root@centos8 ~]#echo $PI
3.14159
[root@centos8 ~]#exit
logout
Connection closed by foreign host.
Disconnected from remote host(10.0.0.8) at 14:27:04.
Type `help' to learn how to use Xshell prompt.
[c:\~]$
Reconnecting in 1 seconds. Press any key to exit local shell.
.
Connecting to 10.0.0.8:22...
Connection established.
To escape to local shell, press 'Ctrl+Alt+]'.
WARNING! The remote SSH server rejected X11 forwarding request.
Last login: Wed Apr  1 13:51:28 2020 from 10.0.0.1
[root@centos8 ~]#echo $PI
[root@centos8 ~]#
​

1.2.5 位置变量

$1,$2,... #对应第1个、第2个等参数,shift [n]换位置
$0 #命令本身,包括路径
$* #传递给脚本的所有参数,全部参数合为一个字符串
$@ #传递给脚本的所有参数,每个参数为独立字符串
$# #传递给脚本的参数的个数#注意:$@ $* 只在被双引号包起来的时候才会有差异

清空所有位置变量

[root@centos8 ~]#cat /data/scripts/arg.sh
#!/bin/bash
#
#****************************************************
#Author:           jose
#QQ:               123456
#Date:             2022-08-17
#FileName:         arg.sh
#URL:               http://www.magedu.com
#Description:       test
#Copyright(C):     2022 All right
#***************************************************
echo "1st arg is $1"
echo "2st arg is $2"
echo "3st arg is $3"
echo "10st arg is ${10}"
echo "11st arg is ${11}"
echo "The number of arg is $#"
echo "All args are $*"
echo "All args are $@"
echo "The scriptname is `basename $0`"
[root@centos8 ~]#bash /data/scripts/arg.sh {a..z}
1st arg is a
2st arg is b
3st arg is c
10st arg is j
11st arg is k
The number of arg is 26
All args are a b c d e f g h i j k l m n o p q r s t u v w x y z
All args are a b c d e f g h i j k l m n o p q r s t u v w x y z
The scriptname is arg.sh
​

范例:删库跑路之命令rm的安全实现

rm=/tmp/test.dir/2.rm.test.sh
[root@centos8 ~]#cat /data/scripts/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
[root@centos8 ~]#chmod a+x /data/scripts/rm.sh
[root@centos8 ~]#alias rm='/data/scripts/rm.sh' #添加别名
[root@centos8 ~]#touch {1..10}.txt
[root@centos8 ~]#rm *.txt
Move 10.txt 1.txt 2.txt 3.txt 4.txt 5.txt 6.txt 7.txt 8.txt 9.txt to /tmp/2020-
04-01_15-15-28
​

范例:$*和$@的区别

[root@centos8 scripts]#cat f1.sh
#!/bin/bash
echo "f1.sh:all args are $@"
echo "f1.sh:all args are $*"
./file.sh "$*"
[root@centos8 scripts]#cat f2.sh
#!/bin/bash
echo "f2.sh:all args are $@"
echo "f2.sh:all args are $*"
./file.sh "$@"
[root@centos8 scripts]#cat file.sh
#!/bin/bash
echo "file.sh:1st arg is $1"
[root@centos8 scripts]#./f1.sh a b c
f1.sh:all args are a b c
f1.sh:all args are a b c
范例: 利用软链接实现同一个脚本不同功能
2.7.9 退出状态码变量
当我们浏览网页时,有时会看到下图所显示的数字,表示网页的错误信息,我们称为状态码,在shell脚
本中也有相似的技术表示程序执行的相应状态。
file.sh:1st arg is a b c
[root@centos8 scripts]#./f2.sh a b c
f2.sh:all args are a b c
f2.sh:all args are a b c
file.sh:1st arg is a
​

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

[root@centos8 ~]#cat test.sh
#!/bin/bash
#********************************************************************
echo $0
[root@centos8 ~]#ln -s test.sh a.sh # a.sh 是连接文件(linkname)
[root@centos8 ~]#ln -s test.sh b.sh
[root@centos8 ~]#./a.sh
./a.sh
[root@centos8 ~]#./b.sh
./b.sh

1.2.6 退出状态码变量

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

$?   #0代表成功,1-255代表失败
[root@centos8 ~]#curl -fs http://www.wangxiaochun.com >/dev/null
[root@centos8 ~]#echo $?
0
​

用户可以在脚本中使用以下命令自定义退出状态码

exit [n]

注意:

  • 脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字

  • 如果exit后面无数字,终止退出状态取决于exit命令前面命令执行结果

  • 如果没有exit命令, 即未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一 条命令的状态码

1.2.7 展开命令行

展开命令执行顺序

把命令行分成单个命令词
展开别名
展开大括号的声明{}
展开波浪符声明 ~
命令替换$() 和 ``
再次把命令行分成命令词
展开文件通配符*、?、[abc]等等
准备I/0重导向 <、>
运行命令

防止扩展

\ #反斜线 会使随后的字符按原意解释
[root@centos8 ~]#echo Your cost: \$5.00
Your cost: $5.00
[root@rocky8 ~]#echo "The book's price is \$10"
The book's price is $10
​

加引号来防止扩展

'' #单引号 防止所有扩展
"" #双引号 也可防止扩展,但是以下情况例外 $
​

变量扩展

`` #反引号,命令替换
\ #反斜线,禁止单个字符扩展
! #叹号,历史命令替换

1.3 退出状态码变量

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

$? #0代表成功,1-255代表失败

范例:

ping -c1 -W1 hostdown &> /dev/null
echo $?
[root@centos8 ~]#curl -fs http://www.wangxiaochun.com >/dev/null
[root@centos8 ~]#echo $?
0

用户可以在脚本中使用以下命令自定义退出状态码

exit [n]
#脚本
12 echo "hcdakhksdflhjlfd"13 echo "dsfjhsdlkifhjdsi"14 #exit 119                                                          15 kldafjlaksfj;lsdaf16 exit 123#命令行[root@host2 test]# sh exit.txt.sh
hcdakhksdflhjlfd
dsfjhsdlkifhjdsi
[root@host2 test]# echo $?
119

注意:

  • 脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字

  • 如果exit后面无数字,终止退出状态取决于exit命令前面命令执行结果

  • 如果没有exit命令, 即未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一

  • 条命令的状态码

1.4 展开命令行

防止扩展

[root@centos8 ~]#echo Your cost: \$5.00
Your cost: $5.00
[root@rocky8 ~]#echo "The book's price is \$10"
The book's price is $10

加引号来防止扩展

'' #单引号 防止所有扩展
"" #双引号 也可防止扩展,但是以下情况例外 $

变量扩展

`` #反引号,命令替换
$()#反引号,命令替换
\ #反斜线,禁止单个字符扩展
! #叹号,历史命令替换

1.5 脚本安全和 set

选项 含义 备注
h hashall 开启hash表缓存外部命令路径
i interactivecomments 表示当前是交互是shell
m monitor 开启监控模式,可通过 Job control来控制进程的停止、继续,后台 或者前台执行等
B braceexpand 是否支持大括号扩展
H history 是否可用 ! 快速展开history命令
#查看命令行下的配置
[root@rocky86 0817]# echo $-
himBHs
#范例:关闭hash缓存功能
[root@rocky86 0817]# echo $-
himBHs
[root@rocky86 0817]# hash
hits command
6 /usr/bin/bash
2 /usr/bin/cat
6 /usr/bin/vim
[root@rocky86 0817]# set +h
[root@rocky86 0817]# echo $-
imBHs
[root@rocky86 0817]# hash
-bash: hash: hashing disabled
​

1.6 格式化输出 printf命令

%s 字符串
%d,%i 十进制整数
%f 浮点格式
%c ASCII字符,即显示对应参数的第一个字符
%b
相对应的参数中包含转义字符时,可以使用此替换符进行替换,对应的转义字符会被转
义
%o 八进制值
%u 不带正负号的十进制值
%x 十六进制值(a-f)
%X 十六进制值(A-F)
%% 表示%本身

范例:

[root@centos8 ~]#printf "%s" 1 2 3 4
1234[root@centos8 ~]#
[root@centos8 ~]#printf "%s\n" 1 2 3 4
1
2
3
4
[root@centos8 ~]#printf "%f\n" 1 2 3 4
1.000000
2.000000
3.000000
4.000000
#.2f 表示保留两位小数
[root@centos8 ~]#printf "%.2f\n" 1 2 3 4
1.00
2.00
3.00
4.00
#%-10s 表示宽度10个字符,左对齐
[root@centos8 ~]#printf "%-10s %-10s %-4s %s \n" 姓名 性别 年龄 体重 小明 男 20 70 小
红 女 18 50
姓名 性别 年龄 体重
小明 男 20 70
小红 女 18 50

1.7 echo 命令

echo(显示文字)echo会将输入的字符串送往标准输出,输出的字符串间以空白字符隔开, 并在最后加上换行号;

echo -n 表示要输出的内容不换行

  • 命令语法

    echo -e 打开反斜杠ESC转义;

\a 提示音
\b 删除前一个字符
\c 最后不加上换行符号
\f 换行但光标仍旧停留在原来的位置;
\n 换行且光标移至行首;
\r 光标移至行首,但不换行;
\t 插入空格(tab);
\v 换行但光标仍旧停留在原来的位置,与\f相同;
\ 插入\字符;
\nnn 插入nnn(八进制)所代表的ASCII字符;

echo颜色输出

参数说明

echo -e "\e[1;31mabcd \e[0m"

echo -e "\033[1 ; m…… \033[0m"

\e和\033输出效果相同 ,1 的位置是输出内容背景颜色,m 的位置是输出内容字体颜色,\033[0m 的位置是输出内容的属性

[root@localhost ~]# echo -e "\e[40;37m黑底白字 \e[0m"
​
黑底白字
​
[root@localhost ~]# echo -e "\e[41;37m深红白字 \e[0m"
​
深红白字
​
[root@localhost ~]# echo -e "\e[42;37m绿底白字 \e[0m"
​
绿底白字
​
[root@localhost ~]# echo -e "\e[43;37m黄底白字 \e[0m"
​
黄底白字
​
[root@localhost ~]# echo -e "\e[44;37m蓝底白字 \e[0m"
​
蓝底白字
​
[root@localhost ~]# echo -e "\e[45;37m紫底白字 \e[0m"
​
紫底白字
​
[root@localhost ~]# echo -e "\e[46;37m深绿白字 \e[0m"
​
深绿白字
​
[root@localhost ~]# echo -e "\e[47;30m白底黑字 \e[0m"
​
白底黑字
​

彩色字(随机色)

echo -e "\E[1;$[RANDOM%7+31]m随机色字\E[0m“
#RANDOM%7 表示从1到7随机抽取一个数 echo ${RANDOM%n} n 表示随机的数字的总数。
$RANDOM #取值范围:0-32767

1.8 算术运算

实现算术运算:

let var=expression
((var=expression))
var=$[expression]
var=$((expression))
var=$(expr arg1 arg2 ...)
declare -i var=number
echo expression|bc
[root@rocky86 0817]# i=123;j=456;
[root@rocky86 0817]# let k=i+j;echo $k
579
[root@rocky86 0817]# ((k=i+j));echo $k
579
[root@rocky86 0817]# k=$[i+j];echo $k
[root@rocky86 0817]# expr 10 \* 20
200
[root@rocky86 0817]# echo $[$RANDOM%50]
6
​

增强型赋值:

+= # i+=10 相当于 i=i+10
-= # i-=j 相当于 i=i-j
*=
/=
%=
++ # i++,++i 相当于 i=i+1
-- # i--,--i 相当于 i=i-1

范例:i++与++i

[root@rocky86 0817]# i=1;let j=i++;echo $i $j
2 1
[root@rocky86 0817]# i=1;let j=++i;echo $i $j
2 2

1.9 短路运算

当cmd1 的结果为true时,会执行 cmd2; 当cmd1 的结果为false时,会执行 cmd3; cmd1 || cmd2 && cmd3 这种语法是错误的,不使用;

1.10 条件测试命令

1.10.1 条件测试命令

test [expr]
[ arg... ] #同 test
[[ expression ]] #增加版的[],支持[],支持扩展正则表达式和通配符

注意:[] 中的表达式,前后要有空格

1.10.2 常用选项:文件判断相关

-a FILE #如果文件存在则为真
-b FILE #如果文件为块特殊文件则为真
-c FILE #如果文件为字符特殊文件则为真
-d FILE #如果文件为目录则为真
-e FILE #如果文件存在则为真
-f FILE #如果文件存在且为常规文件则为真
-g FILE #如果文件的组属性设置打开则为真
-h FILE #如果文件为符号链接则为真
-L FILE #如果文件为符号链接则为真
-k FILE #如果文件的粘滞 (sticky) 位设定则为真
-p FILE #如果文件为命名管道则为真
-r FILE #如果当前用户对文件有可读权限为真
-s FILE #如果文件存在且不为空则为真
-S FILE #如果文件是套接字则为真
-t FD #如果文件描述符在一个终端上打开则为真
-u FILE #如果文件设置了SUID特殊权限则为真
-w FILE #如果当前用户对文件有可写权限为真
-x FILE #如果当前用户对文件有可执行权限为真
-O FILE #如果当前用户是文件属主为真
-G FILE #如果当前用户对文件属组为真
-N FILE #如果文件上次被读取之后修改过则为真
FILE1 -nt FILE2 #如果 file1 文件新于 file2 文件则为真(根据修改日
期)
FILE1 -ot FILE2 #如果 file1 文件旧于 file2 文件则为真
FILE1 -ef FILE2 #如果 file1 文件是 file2 文件的硬链接则为真

范例

[root@host2 test]# if [ -d /etc ];then echo "caojidong 666666" ;fi
caojidong 666666

1.10.3 常用选项:字符串判断相关

-z STRING #判断字符串是否为空,为空则为真
-n STRING #如果字符串不为空则为真
STRING #同上
#两个字符串变量比较,一定要有空格,如果没有空格,就变成赋值了
STRING1 = STRING2 #如果 string1 和 string2 字符串相同则为真
STRING1 != STRING2 #如果 string1 和 string2 字符串不相同则为真
STRING1 < STRING2 #只比较第一个字符,以字母表顺序来比较,string1在
string2 之前则为真,< 要转义
STRING1 > STRING2 #只比较第一个字符,以字母表顺序来比较,string1在
string2 之后则为真, > 要转义

1.10.4 常用选项:数学相关

#数字相关 格式 arg1 OP arg2 # equal unequal  less tan greater than
-eq #等于
-ne #不等于
-lt #小于
-le #小于等于
-gt #大于
-ge #大于等于
#算术表达式比较
== #相等
!= #不相等
<= #小于或等于
>= #大于或等于
< #小于
> #大于

常用选项:其它

-o OPTION #如果在shell 中开启了某项,则为真
-v VAR #如果变量被设置为为真
-R VAR #如果变量被设置且被引用则为真
! EXPR #表达式结果取反
EXPR1 -a EXPR2 #如果 expr1 和 expr2 都为真则为真
EXPR1 -o EXPR2 #如果 expr1 和 expr2 有一个为真则为真

1.10.5 变量测试

范例:判断变量是否被定义

[root@centos8 ~]#unset x
[root@centos8 ~]#test -v x
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#x=10
[root@centos8 ~]#test -v x
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#y=
[root@centos8 ~]#test -v y
[root@centos8 ~]#echo $?
0
#注意 [ ] 中需要空格,否则会报下面错误
[root@centos8 ~]#[-v name]
-bash: [-v: command not found
[root@centos8 ~]#[ -v name ]
[root@centos8

1.10.6 字符串测试

-z STRING #判断字符串是否为空,为空则为
-n STRING #如果字符串不为空则为真
STRING #同上
#两个字符串变量比较,一定要有空格,如果没有空格,就变成赋值了
STRING1 = STRING2 #如果 string1 和 string2 字符串相同则为真
STRING1 != STRING2 #如果 string1 和 string2 字符串不相同则为真
STRING1 < STRING2 #只比较第一个字符,以字母表顺序来比较,string1在
string2 之前则为真,< 要转义
STRING1 > STRING2 #只比较第一个字符,以字母表顺序来比较,string1在
string2 之后则为真, > 要转义
[[ expression ]] 用法
== #左侧字符串是否和右侧的PATTERN相同
#注意:此表达式用于[[ ]]中,PATTERN为通配符
=~ #左侧字符串是否能够被右侧的正则表达式的PATTERN所匹配
#注意: 此表达式用于[[ ]]中为扩展的正则表达式

范例:在比较字符串时,建议变量放在“ ”中

[root@centos8 ~]#[ "$NAME" ]
[root@centos8 ~]#echo $?
0

范例:比较符要转义

#这个才是正确的
[root@rocky86 ~]# a=a;b=b; [ "$a" \> "$b" ]; echo $?
1
[root@rocky86 ~]# a=b;b=a; [ "$a" \> "$b" ]; echo $?

范例: [[ ]] 和通配符

[root@centos8 ~]#[[ $FILE == a"*" ]]
[root@centos8 ~]#echo $?
0

范例: 判断合理的考试成绩

[root@centos8 script]#SCORE=101
[root@centos8 script]#[[ $SCORE =~ ^(100|[0-9]{1,2})$ ]]
[root@centos8 script]#echo $?
1
[root@centos8 script]#SCORE=10
[root@centos8 script]#[[ $SCORE =~ ^(100|[0-9]{1,2})$ ]]
[root@centos8 script]#echo $?
0
[root@centos8 script]#SCORE=abc
[root@centos8 script]#[[ $SCORE =~ ^(100|[0-9]{1,2})$ ]]
[root@centos8 script]#echo $?
1

范例:使用 [[ ]] 判断文件后缀

#通配符
[root@centos8 ~]#FILE=test.log
[root@centos8 ~]#[[ "$FILE" == *.log ]]
[root@centos8 ~]#echo $?
0
#正则表达式
[root@centos8 ~]#[[ "$FILE" =~ \.log$ ]]
[root@centos8 ~]#echo $?
1

范例: 判断合法的非负整数

[root@centos8 ~]#N=100
[root@centos8 ~]#[[ "$N" =~ ^[0-9]+$ ]]
[root@centos8 ~]#echo $?
0

范例: 判断合法IP

[root@centos8 ~]#IP=1.2.3.4
[root@centos8 ~]#[[ "$IP" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]
[root@centos8 ~]#echo $?
范例:
IP=$1
[[ $IP =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]
{2}|2[0-4][0-9]|25[0-5])$ ]] && echo $IP is valid || echo $IP is invalid

范例: -e和-a 表示判断文件的存在性,建议使用-e

#文件是否不存在
[root@centos8 ~]#[ -a /etc/nologin ]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#! [ -a /etc/nologin ]
[root@centos8 ~]#echo $?
0
文件权限测试:
注意:最终结果由用户对文件的实际权限决定,而非文件属性决定
范例:
#文件是否存在
[root@centos8 ~]# [ -a /etc/issue ]
[root@centos8 ~]#echo $?
0
#取反后结果却没有变化
[root@centos8 ~]# [ ! -a /etc/issue ]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#! [ -a /etc/issue ]
[root@centos8 ~]#echo $?
1
#文件是否存在
[root@centos8 ~]#! [ -e /etc/issue ]
[root@centos8 ~]#echo $?
1
#此为推荐写法
[root@centos8 ~]#[ ! -e /etc/issue ]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#[ -d /etc ]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#[ -d /etc/issue ]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#[ -L /bin ]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#[ -L /bin/ ]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#[ -w /etc/shadow ]
[root@centos8 ~]#echo $?
0
[root@

1.11 关于 () 和 {}

( cmd1;cmd2;... ) 和 { cmd1;cmd2;...; } 都可以将多个命令组合在一起,批量执行 ( list ) 会开启子shell,并且list中变量赋值及内部命令执行后,将不再影响后续的环境 { list; } 不会开启子shell, 在当前shell中运行,会影响当前shell环境,左侧要有空格,右侧要有; 结束

1.12 组合测试条件

[ EXPRESSION1 -a EXPRESSION2 ] #并且,EXPRESSION1和
EXPRESSION2都是真,结果才为真
[ EXPRESSION1 -o EXPRESSION2 ] #或者,EXPRESSION1和
EXPRESSION2只要有一个真,结果就为真
[ ! EXPRESSION ] #取反

说明: -a 和 -o 需要使用测试命令进行,[[ ]] 不支持

条件组合

COMMAND1 && COMMAND2 || COMMAND3

轮盘游戏

[root@centos7 ~]#[ $[RANDOM%6] -eq 0 ] && rm -rf /* || echo "Luckly boy"

范例:磁盘空间的判断

WARNING=80
SPACE_USED=`df|grep '^/dev/sd'|tr -s ' ' %|cut -d% -f5|sort -nr|head -1`[
"$SPACE_USED" -ge $WARNING ] && { echo "disk used is $SPACE_USED,will be full" ;
mail -s diskwarning root; }

范例:磁盘空间和Inode号的检查脚本

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

2 bash shell 的配置文件

全局配置:针对所有用户皆有效

/etc/profile
/etc/profile.d/*.sh
/etc/bashrc

个人配置:只针对特定用户有效

~/.bash_profile
~/.bashrc

2.1 shell登录两种方式分类

1)交互式登录

  • 直接通过终端输入账号密码登录

  • 使用 su - UserName 切换的用户

配置文件生效和执行顺序:

#放在每个文件最前
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
~/ .bash_ profile
~/ .bashrc
/etc/bashrc #此文件执行两次
#放在每个文件最后
/etc/profile.d/*.sh
/etc/bashrc
/etc/profile
/etc/bashrc #此文件执行两次
~/.bashrc
~/.bash_profile

注意:文件之间的调用关系,写在同一个文件的不同位置,将影响文件的执行顺序

2.2 按功能划分分类

内容 Profile Bashrc
交互式登录shell配置
非交互式登录shell配置
全局配置文件 /etc/profile,/etc/profile.d/*.sh /etc/bashrc
个人配置文件 ~/.bash_profile ~/.bashrc
功能作用 定义环境变量,运行命令或脚本 定义命令别名和函数,定义本地变量

2.3 编辑配置文件生效

修改profile和bashrc文件后需生效两种方法:

重新启动shell进程

source|. 配置文件注意:source 会在当前shell中执行脚本,所有一般只用于执行置文件,或在脚本中调用另一个脚本的场景 范例:

. ~/.bashrc

2.4 Bash 退出任务

保存在~/.bash_logout文件中(用户),在退出登录shell时运行 功能:

  • 创建自动备份

  • 清除临时文件

3 流程控制

i++ 即后加加,原理是:先自增,然后返回自增之前的值
++i 即前加加,原理是:先自增,然后返回自增之后的值
重点:这是一般人所不知道的,记住:不论是前++还是后++,都有个共同点是先自增。
对于++i 就不说了,大多数人都懂,而对于 i++ 的原理,我用代码模拟其原理,如下:

3.1 选择执行 if 语句

if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else
COMMANDS; ] fi

单分支

if 判断条件;then
条件为真的分支代码
fi

双分支

if 判断条件; then
条件为真的分支代码
else
条件为假的分支代码
fi

多分支

if 判断条件1; then
条件1为真的分支代码
elif 判断条件2; then
条件2为真的分支代码
elif 判断条件3; then
条件3为真的分支代码
...
else
以上条件都为假的分支代码
fi

说明:

  • 多个条件时,逐个条件进行判断,第一次遇为“真”条件时,执行其分支,而后结束整个if语句

  • if 语句可嵌套

范例:判断操作系统

. /etc/os-release
if [ $ID = "ubuntu" ]; then
echo "ubuntu"
elif [[ $ID =~ rocky|centos|rhel ]]; then
echo "rhel"
else
echo "other os"
exit 100
fi

范例:身体质量指数 (BMI)

read -p "请输入身高(m为单位): " HIGH
if [[ ! "$HIGH" =~ ^[0-2](\.[0-9]{1,2})?$ ]];then
echo "输入错误的身高!"
exit 1
fi
read -p "请输入体重(kg为单位): " WEIGHT
if [[ ! "$WEIGHT" =~ ^[0-9]{1,3}$ ]];then
echo "输入错误的体重!";
exit 2;
fi
BMI=`echo $WEIGHT/$HIGH^2|bc`
if [ $BMI -le 18 ] ;then
echo "太瘦了,多吃点!"
elif [ $BMI -lt 24 ] ;then
echo "身材很棒!"
else
echo "太胖了,注意节食,加强运动!"
fi

3.2 条件判断 case 语句

case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac
case支持glob风格的通配符:
范例:判断用户输入
case 变量引用 in
PAT1)
分支1
;;
PAT2)
分支2
;;
...
*)
默认分支
;;
esac

case支持glob风格的通配符:

* #任意长度任意字符
? #任意单个字符
[] #指定范围内的任意单个字符
| #或者,a|b

范例:判断用户输入

read -p "Do you agree(yes/no)? " INPUT
INPUT=`echo $INPUT | tr 'A-Z' 'a-z'`
case $INPUT in
y|yes)
echo "You input is YES"
;;
n|no)
echo "You input is NO"
;;
*)
echo "Input fales,please input yes or no!"
;;
范例:判断用户输入
范例: 文件后缀处理
esac

3.3 循环的命令 for命令

3.3.1 方法 一

程序先进行语句判断,如真则执行循环语句,然后再进行语句判断,直至语句判断失败才跳出;

#方式1
for 变量名 in 列表;do
循环体
done
#

范例: 100以内的奇数之和

[root@centos8 ~]#sum=0;for i in {1..100..2};do let sum+=i;done;echo sum=$sum
sum=2500
[root@centos8 ~]#seq -s+ 1 2 100| bc
2500
[root@centos8 ~]#echo {1..100..2}|tr ' ' + | bc
2500

范例:从变量中获取列表

sum=0
for i in $* ; do
let sum+=i
done
echo sum=$sum
[root@centos8 ~]#bash /data/scripts/for_sum.sh 1 2 3 4 5 6
sum=21

范例: 批量创建用户

[ $# -eq 0 ] && { echo "Usage: createuser.sh USERNAME ..." ;exit 1 ; }
for user ;do
id $user &> /dev/null && echo $user is exist || { useradd $user ; echo $user
is created; }
done

3.3.2 方法二

for (( exp1; exp2; exp3 )); do COMMANDS; done
for ((控制变量初始化;条件判断表达式;控制变量的修正表达式))
do
循环体
done
for((sum=0,i=1;i<=100;i++));do
let sum+=i
done
echo sum=$sum
for((sum=0,i=1;i<=100;sum+=i,i++));do
true
done
echo sum=$su

99 乘法表

for((i=1;i<10;i++));dofor((j=1;j<=i;j++));doecho -e "${j}x$i=$((j*i))\t\c"done
echo
done

3.4 循环 while

while COMMANDS; do COMMANDS; done
while CONDITION; do
循环体
done
while CONDITION
do
循环体
done

3.4.1 无限循环

while true; do
循环体
done
while : ; do
循环体
done

范例:

[root@centos8 ~]#sum=0;i=1;while ((i<=100));do let sum+=i;let i++;done;echo $sum
5050
​
WARNING=80
while :;do
USE=`df | sed -rn '/^\/dev\/sd/s#.* ([0-9]+)%.*#\1#p' |sort -nr|head -n1`
if [ $USE -gt $WARNING ];then
范例: 国际象棋棋盘
echo Disk will be full from `hostname -I` | mail -s "disk warning"
29308620@qq.com
fi
sleep 10
done

3.4.2 while 特殊用法 while read

while 循环的特殊用法,遍历文件或文本的每一行

格式:

while read line; do
循环体
done < /PATH/FROM/SOMEFILE

说明:依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将行赋值给变量line

范例:检测网站是否可以访问

[root@rocky86 ~]# cat url.txt
http://www.baidu.com
http://www.google.com
http://www.facebook.com
http://www.jd.com
[root@rocky86 ~]# cat check_url.sh
while read url;do
curl $url &> /dev/null || echo "$url fail"
done < url.txt
#管道写法
cat url.txt | while read url;do
curl $url &> /dev/null || echo "$url fail"
done
​

3.4.3 循环 until(反的while)

until CONDITION; do COMMANDS; done
until CONDITION; do
循环体
done

说明: 进入条件: CONDITION 为false 退出条件: CONDITION 为true

无限循环

until false; do
循环体
Done

3.5 循环与菜单 select

格式:

select NAME [in WORDS ... ;] do COMMANDS; done
select NAME in list ;do
循环体命令
done
PS3="请选择: "
select i in 备份数据库 清理日志 重启服务 退出; do
case $REPLY in
1)
5 函数 function
5.1 函数介绍
函数 function
是由若干条shell命令组成的语句块,实现代码重用和模块化编程
它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell程序的一部
分
函数和shell程序区别
Shell程序在子Shell中运行
echo "开始备份数据库..."
sleep 2
echo "数据库备份完成"
;;
2)
echo "开始清理日志..."
sleep 2
echo "清理完成"
;;
3)
echo "开始重启服务..."
sleep 2
echo "重启完成"
;;
4)
break
;;
*)
echo "选项错误"
;;
esac
done

3.6 控制语句

3.6.1 循环控制语句 continue

continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断;最内层为第1层 格式:

while CONDITION1; do
CMD1
...
if CONDITION2; then
continue
fi
CMDn
...
done

3.6.2 循环控制语句 break

break [N]:提前结束第N层整个循环,最内层为第1层 格式:

while CONDITION1; do
CMD1
...
if CONDITION2; then
break
fi
CMDn
...
done

break 2(跳出两层)

3.6.3 循环控制 shift 命令

shift [n] 用于将参量列表 list 左移指定次数,缺省为左移一次。 参量列表 list 一旦被移动,最左端的那个参数就从列表中删除。while 循环遍历位置参量列表时,常用到 shift

until [ -z "$1" ]; do
echo "$@ "
shift
done
[root@rocky86 ~]# bash s.sh a b c d e f g
a b c d e f g
b c d e f g
c d e f g
d e f g
e f g
f g
g

4 函数 function

4.1 函数介绍

函数 function 是由若干条shell命令组成的语句块,实现代码重用和模块化编程 它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell程序的一部 分

函数和shell程序区别

Shell程序在子Shell中运行 echo "开始备份数据库..." sleep 2 echo "数据库备份完成" ;; 2) echo "开始清理日志..." sleep 2 echo "清理完成" ;; 3) echo "开始重启服务..." sleep 2 echo "重启完成" ;; 4) break ;; *) echo "选项错误" ;; esac done 函数在当前Shell中运行。因此在当前Shell中,函数可对shell中变量进行修改

4.2.1 定义函数

#语法一
func_name(){...函数体...
}
#语法二
function func_name {...函数体...
}
#语法三
function func_name(){...函数体...
}
​

4.2.2 查看函数

#查看当前已定义的函数名
declare -F
#查看当前已定义的函数定义
declare -f
#查看指定当前已定义的函数名
declare -f func_name
#查看当前已定义的函数名定义
declare -F func_name

4.2.3 删除函数

unset func_name

4.3 函数调用

4.3.2 在脚本中定义及使用函数

[root@rocky86 ~]# cat func1.sh
#!/bin/bash
#
#****************************************************
#Author: jose
#QQ: 123456
范例:函数调用函数
#Date: 2022-08-19
#FileName: func1.sh
#URL: http://www.magedu.com
#Description: test
#Copyright(C): 2022 All right
#***************************************************
#函数定义
hello(){echo "this is test function - hello"
}
#第一次调用
hello
#第二次调用
hello

4.4 函数返回值

函数默认返回值是函数体中最后一条命令的退出状态码; 也可以使用return 自定义函数的返回值,在函数中使用 return,return 之后的语句将不会被执行 return 的用法:

return 语句 返回值

return 由return 语句的前一行命令执行结果决定

return 0 无错误返回

return 1-255 有错误返回

范例:获取函数返回值

var_func1(){echo "func1 ======="
范例:return 和 exit 的区别
return 只会中断函数执行,但exit 会中断整个脚本的执行
}
#默认返回值
var_func2(){echo "func2 ======="return
}
var_func3(){echo "func3 ======"errcmd
}
#自定义返回值,return 之后的语句将不会被执行
var_func4(){echo "func4 aaaaaaaaaaaaaa"return 123echo "func4 bbbbbbbbbb"
}
var_func1
echo -e "func1 返回值是 $?\n"
var_func2
echo -e "func2 返回值是 $?\n"
var_func3
echo -e "func3 返回值是 $?\n"
var_func4
echo -e "func4 返回值是 $?\n"
#执行
[root@rocky86 0820]# bash var4.sh
func1 =======
func1 返回值是 0
func2 =======
func2 返回值是 0
func3 ======
var4.sh: line 26: errcmd: command not found
func3 返回值是 127
func4 aaaaaaaaaaaaaa
func4 返回值是 123

范例:return 和 exit 的区别

var_func1(){echo "func1 ======="return 123echo "func1 ======="
}
echo 123
var_func1
echo "func1 返回值是 "$?
echo 456
[root@rocky86 0820]# bash var5.sh
123
func1 =======
func1 返回值是 123
456
​

4.5 环境函数

类拟于环境变量,也可以定义环境函数,使子进程也可使用父进程定义的函数 定义环境函数:

export -f function_name
declare -xf function_name
​

查看环境函数:

export -f
declare -xf

4.6 函数递归

函数递归: 函数直接或间接调用自身,注意递归层数,可能会陷入死循环 递归特点: 函数内部自已调用自已 必须有结束函数的出口语句,防止死循环 范例:无出口递归

[root@rocky86 ~]# num=1;cli_func1(){ echo "num="$num; let num++; cli_func1;
};cli_func1
......
......
num=8587
Connection closed.
Disconnected from remote host(rocky86-150) at 11:44:30.
Type `help' to learn how to use Xshell prompt.
​

用递归实现阶乘

fac(){if [ $1 -gt 1 ];thenecho $[$1 * $(fac $[$1-1])]elseecho 1fi
}
fac $1
[root@rocky86 ~]# bash fac.sh 5
12

逻辑炸弹

:(){ :|:& };:
bomb(){ bomb | bomb & };bomb

5 其它脚本相关工具

5.1 信号捕捉 trap

trap 命令可以捕捉信号,修改信号原来的功能,实现自定义功能 在脚本或程序的执行过程中,我们可以通过发送信号的方式,打断或终止程序的执行过程,为了避免这 种情况,我们可以使用信号捕捉,来自定义信号处理。

格式:

trap [-lp] [[arg] signal_spec ...]
#常用选项
-l #显示所有信号
-p #显示所有自定义的信号
trap 'command' signal #自定义指定信号的处理方式
trap '' signal #忽略指定的信号
trap '-' signal #恢复信号默认操作
trap func EXIT #退出时执行func
​

查看所有信号:

[root@rocky86 ~]# trap -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
......
......
​

信号的三种表示方法

3) SIGQUIT
3 #信号ID
SIGQUIT #完整写法,大小写都支持
QUIT #简短写法,大小写都支持

范例:

trap "echo '捕捉到了 ctrl+c信号'" int
trap "echo '捕捉到了 ctrl+\信号'" quit
trap -p
for i in {1..15};dosleep 1echo $i
done
trap '' int
trap -p
for i in {16..30};dosleep 1
​

5.2 创建临时文件 mktemp

mktemp 命令用于创建并显示临时文件,可避免冲突

范例:

[root@rocky86 ~]# mktemp
/tmp/tmp.YQXxc3bHdI
[root@rocky86 ~]# mktemp XXXX
UYtW
#如果指定了文件名,则后面一定要有X,至少要3个,X会被替换成随机串
[root@rocky86 ~]# mktemp test
mktemp: too few X's in template ‘test’
[root@rocky86 ~]# mktemp testXXX
test19C
#创建并赋值给变量
[root@rocky86 ~]# tmp=`mktemp`
[root@rocky86 ~]# echo $tmp
/tmp/tmp.wQAdrqn1UR
#创建目录
[root@rocky86 ~]# mktemp -d
/tmp/tmp.2iLac1ruHt
[root@rocky86 ~]# ll /tmp/tmp.2iLac1ruHt/
total 0
​

6.3 安装复制文件 install

#如果目录不存在,则创建目录,并将权限设成777,如果目录存在,则修改其权限
[root@rocky86 ~]# install -m 777 -d testdir
[root@rocky86 ~]# ll -d testdir
drwxrwxrwx. 2 root root 6 May 25 12:46 testdir
#将文件复制到指定目录,并指定属主属组,权限
[root@rocky86 ~]# install -m 777 -o mage -g root fin.sh /tmp/fin-tmp.sh
[root@rocky86 ~]# ll /tmp/fin-tmp.sh
-rwxrwxrwx. 1 mage root 153 May 25 12:58 /tmp/fin-tmp.sh
​

6 数组 array

#普通数组可以不事先声明,直接使用
declare -a ARRAY_NAME
#关联数组必须先声明,再使用
declare -A ARRAY_NAME

6.3 数组赋值

[root@rocky86 ~]# weekdays[0]="Sunday"
[root@rocky86 ~]# weekdays[4]="Thursday"

范例:

[root@rocky86 ~]# title=("ceo" "coo" "cto")
[root@rocky86 ~]# num=({0..10})
[root@rocky86 ~]# alpha=({a..g})
[root@rocky86 ~]# file=( *.sh )

6.4 显示所有数组

格式:

declare -a
[root@rocky86 ~]# declare -a
declare -a ARRAY_NAME=([0]="VALUE")
declare -a BASH_ARGC=()
declare -a BASH_ARGV=()
declare -a BASH_COMPLETION_VERSINFO=([0]="2" [1]="7")
declare -a BASH_LINENO=()
declare -ar BASH_REMATCH=()
declare -a BASH_SOURCE=()
declare -ar BASH_VERSINFO=([0]="4" [1]="4" [2]="20" [3]="1" [4]="release"
[5]="x86_64-redhat-linux-gnu")
declare -a DIRSTACK=()

6.5 引用数组

[root@rocky86 ~]# declare -a title=([0]="ceo" [1]="coo" [2]="cto")
[root@rocky86 ~]# echo ${title[1]}
coo
[root@rocky86 ~]# echo ${title}
ceo
[root@rocky86 ~]# echo ${title[2]}
cto
[root@rocky86 ~]# echo ${title[3]}
[root@rocky86 ~]#
​

区分这三种写法

[root@rocky86 ~]# echo $title
ceo
[root@rocky86 ~]# echo ${title[0]}
ceo
[root@rocky86 ~]# echo $title[0]
ceo[0]

引用数组所有元素

格式:

${ARRAY_NAME[*]}
${ARRAY_NAME[@]}
​

遍历数组

[root@rocky86 ~]# arr=({a..e})
[root@rocky86 ~]# for i in ${arr[*]};do echo $i; done;
a
b
数组的长度,即数组中元素的个数
格式:
范例:
数组的所有下标
格式:
c
d
e

数组的长度,即数组中元素的个数

[root@rocky86 ~]# echo ${#title[*]}
3
​

数组的所有下标

[root@rocky86 ~]# echo ${!title[@]}
0 1 2
[root@rocky86 ~]# echo ${!title[*]}
0 1 2

6.6 删除数组

[root@rocky86 ~]# echo ${title[*]}
ceo coo cto
[root@rocky86 ~]# unset title[1]
[root@rocky86 ~]# echo ${title[*]}
ceo cto

6.8 关联数组

关联数组与普通数组区别: 关联数组要先声明,才能使用,普通数组可以不用声明 关联数组可以自定义下标,普通数组必须用数字

declare -A ARRAY_NAME
ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2‘...)

范例:必须先声明

[root@rocky86 ~]# test[a]=123
[root@rocky86 ~]# echo ${test[*]}
123
[root@rocky86 ~]# test[b]=456
[root@rocky86 ~]# echo ${test[*]}
范例:关联数组赋值必须指定下标
456
[root@rocky86 ~]#
#此处再声明就失败,因为test现在是普通数组
[root@rocky86 ~]# declare -A test
-bash: declare: test: cannot convert indexed to associative array
#删除之后重新赋值
[root@rocky86 ~]# unset test
[root@rocky86 ~]# declare -A test
[root@rocky86 ~]# test[ceo]=mage
[root@rocky86 ~]# test[coo]=zhang
[root@rocky86 ~]# echo ${test[ceo]}
mage
[root@rocky86 ~]# echo ${test[cto]}
[root@rocky86 ~]# echo ${test[coo]}
zhang
[root@rocky86 ~]# echo ${test[*]}
mage zhang
#遍历
[root@rocky86 ~]# for i in ${test[*]};do echo ${i};done
mage
zhang
#遍历下标,根据下标取元素内容
[root@rocky86 ~]# for i in ${!test[*]};do echo ${test[$i]};done
mage
zhang
​

6.9 范例

[root@rocky86 ~]# cat rand.sh
#!/bin/bash
declare -i min max
declare -a nums
for((i=0;i<10;i++));donums[$i]=$RANDOMif [ $i -eq 0 ];thenmin=${nums[0]}max=${nums[0]}elseif [ ${nums[$i]} -gt $max ]; thenmax=${nums[$i]}fiif [ ${nums[$i]} -lt $min ]; thenmin=${nums[$i]}fifi
done
echo "All numbers are ${nums[*]}"
echo Max is $max
echo Min is $min

6.9.1 间接变量引用

如果第一个变量的值是第二个变量的名字,从第一个变量引用第二个变量的值就称为间接变量引用 variable1的值是variable2,而variable2又是变量名,variable2的值为value,间接变量引用是指通过 variable1获得变量值value的行为

[root@rocky86 ~]# var1=str
[root@rocky86 ~]# str=abcd
[root@rocky86 ~]# echo $var1
str
[root@rocky86 ~]# echo \$$var1
$str
[root@rocky86 ~]# eval echo \$$var1
abcd
​

bash Shell提供了两种格式实现间接变量引用

#方法1
#变量赋值
eval tempvar=\$$variable1
#显示值
eval echo \$$variable1
eval echo '$'$variable1
echo $tmpvar
#方法2
#变量赋值
tempvar=${!variable1}
#显示值
echo ${!variable1}

面试题:下面脚本执行结果输出什么

[root@rocky86 ~]# cat var123.sh
#!/bin/bash
#
#****************************************************
#Author: jose
#QQ: 123456
#Date: 2022-08-22
#FileName: var123.sh
#URL: http://www.magedu.com
#Description: test
#Copyright(C): 2022 All right
#***************************************************
var(){local var="$1"echo "${!var}"
}
var 1 a
var 2 a b
var 3 a b c
[root@rocky86 ~]# bash var123.s

7 字符串处理

7.1 字符串切片

${#var} #返回字符串变量var的字符的长度,一个汉字算一个字符
${var:offset} #返回字符串变量var中从第offset个字符后(不包括第offset个字
符)的字符开始,到最后的部分,offset的取值在0 到 ${#var}-1 之间(bash4.2后,允许为负值)
${var:offset:number} #返回字符串变量var中从第offset个字符后(不包括第offset个字
符)的字符开始,长度为number的部分
${var: -length} #取字符串的最右侧几个字符, 注意:冒号后必须有一空白字符
${var:offset:-length} #从最左侧跳过offset字符,一直向右取到距离最右侧lengh个字符之
前的内容,即:掐头去尾
${var: -length:-offset} #先从最右侧向左取到length个字符开始,再向右取到距离最右侧
offset个字符之间的内容,注意:-length前空格,并且length必须大于offset

shell 脚本案例

1 显示指标(括CPU型号,内存大小,硬盘大小,操作系统版本)

需求:

写一个脚本,获取主机系统信息,包括CPU型号,内存大小,硬盘大小,操作系统版本这四个指标

我写的

#!/bin/bash
#
#****************************************************
#Author:           caojidong
#QQ:               1549396190
#Date:             2022-09-28
#FileName:         obtsysinfo.test.sh
#cell-phone number:               13739548267
#Description:      获取主机系统信息,包括CPU型号,内存大小,硬盘大小,操作系统版本这四个指标
#Copyright(C):     2022 All right
#***************************************************
cuptye=$(lscpu |sed -En "/^Model name/s/.*([0-9]{2}th.*)/\1/p")
sizeblk=`lsblk | head -n 3 | tail -1| tr -s " " |cut -d" " -f4`
sizefree=`free -h | grep -E  "^Mem:"| tr -s " "| cut -d' ' -f2`
COLORN="\e[31m"
COLOREND="\e[0m"
​
echo "#########################"
echo -e "    硬盘大小是:\t ${COLORN}${sizeblk}${COLOREND}"
echo -e "    内存大小是:\t ${COLORN}${sizefree}${COLOREND}"
echo -e "操作系统版本是:         ${COLORN}`uname -r`${COLOREND}"
echo -e "     CPU大小是:\t ${COLORN}${cuptye}${COLOREND}"
echo "#########################"
echo -e "${COLORN}---------------${COLOREND}"
​

老师写的

#版本1
lscpu | sed -nr 's/^Model name: +(.*)/\1/p'
cat /proc/meminfo | head -n 1 | tr -s " " | cut -d" " -f2,3
lsblk /dev/sda | grep "^sda" | tr -s " " | cut -d" " -f4
cat /etc/os-release | sed -nr "s/^VERSION=\"(.*)\"/\1/p"
[root@rocky86 ~]# bash sysinfo-v1.sh
11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz
1828236 kB
200G
8.6 (Green Obsidian)
​
#版本2
echo -e "CPU   \c"
lscpu | sed -nr 's/^Model name: +(.*)/\1/p'
echo -e "Mem   \c"
cat /proc/meminfo | head -n 1 | tr -s " " | cut -d" " -f2,3
echo -e "DISK   \c"
lsblk /dev/sda | grep "^sda" | tr -s " " | cut -d" " -f4
echo -e "OS \c"
cat /etc/os-release | sed -nr "s/^VERSION=\"(.*)\"/\1/p"
​
#版本3
echo -e "========================= sysinfo begin ================\n"
echo -e "CPU   \c"
echo -e "\E[1;32m `lscpu | sed -nr 's/^Model name: +(.*)/\1/p'` \E[0m"
echo -e "Mem   \c"
echo -e "\E[1;32m `cat /proc/meminfo | head -n 1 | tr -s " " | cut -d" " -f2,3`
\E[0m"
echo -e "DISK   \c"
echo -e "\E[1;32m `lsblk /dev/sda | grep "^sda" | tr -s " " | cut -d" " -f4`
\E[0m"
echo -e "OS \c"
echo -e "\E[1;32m `cat /etc/os-release | sed -nr 's/^VERSION=\"(.*)\"/\1/p'`
\E[0m"
echo -e "\n========================= sysinfo end =================="
​

2.编写脚本 backup.sh,可实现每日将 /etc/ 目录备份到 /backup/etcYYYY-mm-dd中

mkdir -p /backup ;tree -fid --noreport /etc/ > /backup/`date +%W_%T`.etcback.txt

3.编写脚本 disk.sh,显示当前硬盘分区中空间利用率最大的值

DiskUtilization=`df -h | grep -E "^/dev"| head -2| sort -t" " -rk5|head -1|sed -nE "s/.* ([0-9]{1,2}%).*/\1/p" `;echo "硬盘分区中空间利用率最大的值:${DiskUtilization}"

4.编写脚本 links.sh,显示正连接本主机的每个远程主机的IPv4地址和连接数,并按连接数从大到小 排 序

w -h | tr -s " " | cut -d " " -f 3 | sort | uniq -c | sort -nr

SHELL/SSH基础知识(入门篇)-包含 shell 脚本语言的基本用法、 shell 脚本语言的基本用法、流程控制、函数 function、其它脚本相关工具、数组 array(欢迎留言交流)相关推荐

  1. JavaScript 基础知识 - 入门篇(二)

    11. 函数 11.1 函数的基础知识 为什么会有函数? 在写代码的时候,有一些常用的代码需要书写很多次,如果直接复制粘贴的话,会造成大量的代码冗余: 函数可以封装一段重复的javascript代码, ...

  2. C语言基础知识入门和C语言入门基础知识大全

    一.C语言基础知识入门 C语言一出现,就以其丰富的功能.强大的表达能力.灵活性.方便性和广泛的应用,在世界范围内迅速普及和普及.C语言不仅高效而且可移植.它可以用来开发应用软件.驱动程序.操作系统等. ...

  3. Shell 的基础知识

    Shell 的基础知识 终端(terminal) ctrl + alt + T shell : 人机交互接口,解释器sh bash zsh,负责外界与Linux内核交互 cat /etc/shells ...

  4. 基础知识:篇4-make工具与Makefile文件概念

    说明:   本文章旨在总结备份.方便以后查询,由于是个人总结,如有不对,欢迎指正:另外,内容大部分来自网络.书籍.和各类手册,如若侵权请告知,马上删帖致歉.   QQ 群 号:513683159 [相 ...

  5. SQL数据库基础知识-巩固篇一

    SQL数据库基础知识-巩固篇<一>... 首先展示两款我个人很喜欢的数据库-专用于平时个人SQL技术的练习<特点:体积小,好安装和好卸载,功能完全够用了> MySQL-57 D ...

  6. 渗透基础知识入门(自学笔记)

    渗透基础知识入门 学渗透,要学的东西还是比较广,需要有一些基础知识作铺垫开始学,而且作为一个刚踏上学习道路不久的小伙子更应该在学习道路中不断补充更多基础知识,如编程语言,网络知识等,该篇笔记做一些基础 ...

  7. 2022最新知识,一步一步学习C语言基础知识入门(图例解析)

    一.C语言基础知识入门 C语言一经出现就以其功能丰富.表达能力强.灵活方便.应用面广等特点迅速在全世界普及和推广.C语言不但执行效率高而且可移植性好,可以用来开发应用软件.驱动.操作系统等.C语言也是 ...

  8. JavaScript 基础知识 - BOM篇

    前言 本篇文章是JavaScript基础知识的BOM篇,如果前面的<JavaScript基础知识-DOM篇>看完了,现在就可以学习BOM了. 注意: 所有的案例都在这里链接: 提取密码密码 ...

  9. 学习笔记:Java 并发编程①_基础知识入门

    若文章内容或图片失效,请留言反馈. 部分素材来自网络,若不小心影响到您的利益,请联系博主删除. 视频链接:https://www.bilibili.com/video/av81461839 视频下载: ...

最新文章

  1. 编写一个自己的完全C语言版本的memset函数,并且评价这个实现的性能和可移植性。
  2. 微信的充值页面为啥长这样?(多图)
  3. VS2010 VC Project的default Include设置
  4. 【Python学习】 - Matplotlib二维绘图 - plt.matshow()和plt.imshow()区别对比
  5. 阿里P8架构师谈:分布式系统全局唯一ID简介、特点、5种生成方式
  6. 显示封装_LED显示封装高度集成化道路上,五大封装技术谁能率先拔得头筹?
  7. ctags 的最简单使用
  8. C语言删除字符数组中指定的字符(C笔记)
  9. 平面2R机器人的运动学/动力学建模实例
  10. NPDP产品经理小知识:商业模式画布的9个构成要素
  11. Topaz Adjust AI Mac
  12. 【数字图像处理 8】形态学图像处理
  13. 春日游湖不易,但居家聊聊数据湖还是可以的……
  14. 类似QQ的可隐藏的便签工具SNOTE
  15. 如何将720P的mp4视频转换成1080P的视频?视频分辨率如何修改?
  16. Vim编辑器基本操作
  17. UE4元数据关键字的应用与含义(一)
  18. 红帽linux竞赛,浅谈红帽linux挑战赛(三)
  19. Windows多重引导故障快速修复
  20. win11系统右键还原win10菜单和恢复

热门文章

  1. css关于width和height的计算方式
  2. 开放 · 共生 | FISCO BCOS开源三周年庆典邀你相聚
  3. tcmalloc 优化 mysql_利用tcmalloc优化mysql主从
  4. Openlayers Style 样式演示
  5. 无人机技术的发展与应用前景
  6. 腾讯云轻量应用服务器搭建lsky图床并使用KODO云存储
  7. MySQL选错索引导致的线上慢查询事故复盘
  8. 网页设计基础内容总结(一)
  9. tp5 生成静态页面代码
  10. php设计网站页面风格,Photoshop设计简单时尚创意风格的网页首页模板