Shell阶段01 shell脚本规范, shell执行方式, shell变量, shell传参, read传参, shell变量替换, shell脚本调试, shell当前进程和子进程, 退出状态码变量, 脚本安全和set, 变量间接引用
Shell
什么是Shell
Shell就是一个命令解释器。 Shell分为交互式shell和非交互式shell。 交互式Shell就是命令行上面一条一条命令的执行 非交互式Shell就是以脚本的方式运行。 通过变量$-来查看是否是交互式或者非交互式Shell 交互式和非交互式的区别 [root@shell01 ~]# echo $- #命令行上面的执行 himBH [root@shell01 ~]# echo 'echo $-' > test.sh [root@shell01 ~]# sh test.sh #脚本中的执行 hB h #hashall 当Shell运行的时候,会记录保存执行过的命令 hash缓存表 i #interactive 这是交互式shell的意思 m #monitor 监控后台的进程的状态,监控模式(shell中执行命令,命令行上后台进程是看不到的) B #大括号扩展 支持 {} 整体,序列 H #history 历史命令的记录(shell中执行命令是没有记录的)
1.系统命令的堆积 2.特定的格式。特定的语法 组成的一个文件 3.以.sh为结尾的。
自动化的运维
减少不必要的工作
提高工作的效率
1.要对vim文本编辑器熟悉。 .vimrc (vim的个人环境配置文件) 要会运用 在根目录下,编辑.vimrc [root@shell01 ~]# vim .vimrc set tabstop=4 autocmd BufNewFile *.py,*.cc,*.sh,*.java exec ":call SetTitle()" func SetTitle() if expand("%:e") == 'sh' call setline(1,"#!/bin/bash") endif endfunc 2.要有Linux命令的基础应用。 最少要熟悉80个以上的常用linux命令 3.对正则表达式要熟悉,对Awk Sed Grep 三剑客要熟练使用。 4.熟悉常见的linux服务,服务配置,网络,优化,日志等的操作。
1.基础配置 2.安装软件 3.配置变更 4.业务不熟 Git jenkins Shell 进行自动化代码上线 5.日常的备份 通过定时任务+脚本 6.信息收集 Zabbix + Shell 自动化的监控系统的状态 7.日志分析 Elk 8.服务扩容/缩容 Zabbix + Shell
1.脚本统一放在一个目录下 [root@shell01 ~]# mkdir -p /scripts 2.建议使用vim文本编辑器,有高亮显示 3.以.sh为结尾 4.脚本的开头要写#!/bin/bash 指定这个脚本使用什么命令解释器解释脚本中的命令,如果不写,默认是/bin/bash #! 表示的是幻数 这个幻数必须是在脚本中的第一行,在其他行就是个注释信息 5. #号开头的都是注释信息, 写脚本最好加上注释信息 使用英文,不要使用中文,切记不要使用拼音 6.脚本带上作者相关信息 7.成对的符号,要一次书写完成 '' "" 8.格式的语法也要一次书写完成 if ; then fi for i in do done
#shell 脚本注释规范 一般要注明作者,创建(修改)时间,文件名,版本号,该程序的功能及作用,目地,版权信息,作者联系 方式等 root@ubuntu2204 ~]# vim test.sh #!/bin/bash # #**************************************************** #Author: jose #QQ: 123456 #Date: 2022-08-16 #FileName: test.sh #URL: http://www.magedu.com #Description: test #Copyright(C): 2022 All right #**************************************************** echo "hello world" #配置vim 自动生成注释 [root@ubuntu2204 ~]# vim .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: jose") call setline(5,"#QQ: 123456") call setline(6,"#Date: ".strftime("%Y-%m-%d")) call setline(7,"#FileName: ".expand("%")) call setline(8,"#URL: http://www.magedu.com") call setline(9,"#Description: test") call setline(10,"#Copyright(C): ".strftime("%Y")." All right") call setline(11,"#***************************************************") call setline(12,"") call setline(13,"") call setline(14,"") endif endfunc autocmd BufNewFile * normal G
脚本从上到下,从左到右的方式执行。如果报错会继续执行下去。 脚本执行时遇到子脚本,会执行子脚本,子脚本执行完成后,退出子脚本,继续执行脚本中的内容。 脚本运行时,会向系统内核请求一个进程。脚本就会在该进程下进行运行,终止操作。 执行通过哪些方式进行执行: 1. bash script-name 或者 sh script-name #不需要执行权限 [root@shell01 ~]# echo "pwd" > test.sh [root@shell01 ~]# bash test.sh /root [root@shell01 ~]# sh test.sh /root 2. 通过绝对路径或者相对路径执行脚本 path/script-name(绝对路径) 或者 ./script-name #必须哟有执行权限 [root@shell01 ~]# /root/test.sh -bash: /root/test.sh: Permission denied [root@shell01 ~]# chmod u+x test.sh [root@shell01 ~]# ll test.sh -rwxr--r-- 1 root root 4 Apr 22 23:00 test.sh [root@shell01 ~]# /root/test.sh /root [root@shell01 ~]# ./test.sh /root 3. source script-name 或者 . script-name #实际是把脚本里命令调到当前环境(命令行)运行 #无需执行权限 [root@shell01 ~]# chmod -x test.sh # 先去除执行权限 [root@shell01 ~]# ll test.sh -rw-r--r-- 1 root root 4 Apr 22 23:00 test.sh [root@shell01 ~]# source test.sh /root [root@shell01 ~]# . test.sh /root #通过sh执行,能看到后台进程 [root@shell01 ~]# cat test.sh ping baidu.com [root@shell01 ~]# sh test.sh [root@shell01 ~]# ps aux|grep sh # 可以查到sh进程 S+表示可中断状态 ... root 7524 0.0 0.1 113276 1192 pts/0 S+ 23:22 0:00 sh test.sh #通过source执行,查不到后台进程,是在当前环境命令行执行的 [root@shell01 ~]# ps aux|grep source root 7559 0.0 0.0 112808 972 pts/1 R+ 23:25 0:00 grep --color=auto source [root@shell01 ~]# ps aux|grep test root 7561 0.0 0.0 112808 976 pts/1 R+ 23:25 0:00 grep --color=auto test [root@shell01 ~]# ps aux|grep ping # 可以查到ping命令 root 7567 0.0 0.2 150072 2000 pts/0 S+ 23:27 0:00 ping baidu.com
#让shell脚本中的变量生效
[root@web01 scripts]# cat oldboy.sh
name=`whoami`
[root@web01 scripts]# sh oldboy.sh
[root@web01 scripts]# echo $name #没有内容
[root@web01 scripts]# . oldboy.sh
[root@web01 scripts]# echo $name
root
4. cat script-name | bash
#不需要执行权限,将脚本中的命令调入到当前环境下执行
5. bash < script-name
#不需要执行权限,将脚本中的命令调入到当前环境下执行
[root@shell01 ~]# bash < test.sh
登录式 通过用户名和密码的方式进行登录的 非登录式 不是通过用户和密码的方式进行登录的 exit logout exit 可以退出登录式和非登录式Shell logout 只能退出登录式Shell,不能退出非登录时Shell 脚本其实就是一个非登录时Shell,退出脚本要用exit #执行bash命令进去,没有输入密码也是非登录式登录,要通过exit退出
shell脚本调试
执行前先做语法检查 此选项只能检测脚本中的语法错误,不能检测命令错误,也不会执行脚本 bash -n test.sh [root@ubuntu2204 ~]# bash -n test3.sh test3.sh: line 17: unexpected EOF while looking for matching `'' test3.sh: line 18: syntax error: unexpected end of file 调试并执行 逐行输出命令,并输出执行结果 bash -x test.sh [root@ubuntu2204 ~]# bash -x test3.sh + echo 'hello world' hello world + xxxxx test3.sh: line 15: xxxxx: command not found test3.sh: line 17: unexpected EOF while looking for matching `'' test3.sh: line 18: syntax error: unexpected end of file
Shell变量定义
什么是变量? 数据的一种传递方式。 用一个固定的字符串去表示一个不固定的字符串,数值,内容,数据等。 方便后续调用 变量名的规范 建议采用驼峰式方式 Hostname_Ip 变量名由字母,数字,下划线组成,不能使用空格,短横杠,特殊符号尽量不用 见名知意 Shell变量定义的方式 1.用户自定义变量(局部变量) 在当前环境下生效 2.系统环境变量(全局变量): 系统定义好的,主要是定义系统的信息的 按照生存周期划分 永久性 修改配置文件 /etc/profile (一般都配在这里面) 临时性 使用export 变量名称 声明即可
环境变量
可以使子进程(包括孙子进程)继承父进程的变量,但是无法让父进程使用子进程的变量
一旦子进程修改从父进程继承的变量,将会新的值传递给孙子进程
一般只在系统配置文件中使用,在脚本中较少使用
环境变量可以传到子进程里,普通变量不能传到子进程
1)如何定义变量
如何定义环境变量
export name=VALUE
如何定义变量 变量名=变量值 环境变量名称的定义 字符 下划线 数字的组合 尽量以字母开头(禁止使用数字开头) 等号两端不允许有空格 [root@shell01 ~]# name=oldboy #对子shell不生效,对其他连接窗口不生效
如何定义只读变量
只读变量:只能声明定义,但后续不能修改和删除,即常量
readonly name=VALUE
[root@ubuntu2204 ~]# readonly PI=3.14159
[root@ubuntu2204 ~]# echo $PI
3.14159
只读变量无法再次赋值,无法删除,如果实在不要只能退出终端重新进去,然后重新设置
2)如何查看环境变量? 名称的前面加上$ echo $name
显示所有环境变量
env
printenv
export
declare -x
显示已定义的所有变量:
set
查看进程的环境变量
[root@ubuntu2204 ~]# cat /proc/1235/environ
USER=rootLOGNAME=rootHOME=/rootPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/us
r/binSHELL=/bin/bashTERM=linuxSSH_AUTH_SOCK=/tmp/ssh•iIeuAxdLiY/agent.1234XDG_SE
SSION_ID=1XDG_RUNTIME_DIR=/run/user/0DBUS_SESSION_BUS_ADDRESS=unix:path=/run/use
r/0/busSSH_CLIENT=10.0.0.1 1325822SSH_CONNECTION=10.0.0.1 13258
10.0.0.822SSH_TTY=/dev/pts/0
#查看更加工整些
[root@ubuntu2204 ~]# cat /proc/1235/environ |tr '\0' '\n'
USER=root
LOGNAME=root
HOME=/root
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
SHELL=/bin/bash
...
SSH_TTY=/dev/pts/0
3)如何取消环境变量? (一般命令行中才会使用) unset name [root@shell01 ~]# export name=oldboy # 对当前窗口和子shell都生效,对其他连接窗口不生效 #不加export 只对当前的shell生效 #加export 对当前登录窗口所有的shell生效 4)变量值的定义方式 数字的定义 oldboy_age=18 连续的数字 字符串的定义 name="oldboy" 使用双引号(可不加"",但中有空格无法识别) (不知道加什么符号,就加双引号,它还有解析变量作用)
name="$USER" #变量引用 ( $name 或者 ${name} ) name='oldboy' 所见即所得,直接字符串 命令的定义 time=`date +%F` 反引号 里面必须跟可执行命令 time=$(date) $() [root@shell01 ~]# date +%F 2024-04-24 [root@shell01 ~]# time=$(date) [root@shell01 ~]# echo $time Wed Apr 24 00:57:38 CST 2024 #双引号解析作用 [root@shell01 ~]# name=oldboy [root@shell01 ~]# echo "${name}isok" oldboyisok [root@shell01 ~]# echo "$name" oldboy 3.位置参数变量 变量名是固定的,意思也是固定的,通过脚本进行传递数据 $0 脚本的名称,如果全路径执行脚本,则脚本名称也带全路径(basename命令:直接获取脚本名称) 重要 [root@shell01 ~]# cat test.sh #!/bin/bash echo $0 [root@shell01 ~]# sh test.sh test.sh [root@shell01 ~]# chmod u+x test.sh [root@shell01 ~]# /root/test.sh /root/test.sh [root@shell01 ~]# basename /root/test.sh test.sh # $0案例: 给用户提示如何使用此脚本 [root@shell01 ~]# cat test.sh #!/bin/bash echo $"Usage: $0 {start|stop|status|restart|force-reload}" $n 脚本的第n个参数 $0被脚本名称占用 从$1开始 $9以后 需要加{} 重要 [root@shell01 ~]# cat test.sh #!/bin/bash echo $1 $2 $3 $4 ${10} ${11} #$10脚本会识别为$1+$0,需要用{} [root@shell01 ~]# sh test.sh {a..z} a b c d j k $# 代表了脚本的传参的总个数 [root@shell01 ~]# cat test.sh #!/bin/bash echo $# [root@shell01 ~]# sh test.sh {a..z} 26 案例: [root@shell01 ~]# cat test.sh #!/bin/bash #echo $0 [ $# -ne 2 ] && echo "请输入两个参数" && exit #-ne不等于 echo $# [root@shell01 ~]# sh test.sh 请输入两个参数 [root@shell01 ~]# sh test.sh a b 2 $* 脚本的所有的传参的参数 如果不加双引号则和$@相同 加上双引号则把所有参数视为一个整体 $@ 脚本的所有的传参的参数 如果不加双引号则和$*相同 加上双引号则把所有参数视为独立的参数 $* 和 $@ 正常情况下一样 循环体内不同 [root@shell01 ~]# set -- "I am" lizhenya teacher #set设置变量,--为输入都是位置参数 [root@shell01 ~]# for i in $@;do echo $i;done #此时循环有问题,I am分开了 I am lizhenya teacher [root@shell01 ~]# for i in $*;do echo $i;done I am lizhenya teacher [root@shell01 ~]# for i in "$*";do echo $i;done I am lizhenya teacher [root@shell01 ~]# for i in "$@";do echo $i;done I am lizhenya teacher $? 获取上一条命令的结果 0为成功 非0 失败 重要 $$ 获取脚本的PID [root@shell01 ~]# cat test.sh #!/bin/bash echo $$ [root@shell01 ~]# sh test.sh 8001 案例: 服务器运行大量名称相同的脚本,脚本把pid记录到文件中 echo $$ > /tmp/count.pid #杀掉进程 [root@shell01 ~]# kill -9 `cat /tmp/count.pid` $! 上一个在后台运行脚本的PID 调试使用 [root@shell01 ~]# sh test.sh & #后台执行 [root@shell01 ~]# kill -9 $! #杀掉上一个脚本 $_ 获取脚本的最后一个参数 相当于ESC . 4.预定义的变量 脚本中已经定义好的,变量名是固定的,作用也是固定的 $- $? $$ $@ $* 将命令的执行结果赋值给变量 Date = $(date + %F)
利用软链接实现同一个脚本不同功能
[root@ubuntu2204 ~]# cat test.sh #!/bin/bash #******************************************************************** echo $0 [root@ubuntu2204 ~]# ln -s test.sh a.sh [root@ubuntu2204 ~]# ln -s test.sh b.sh [root@ubuntu2204 ~]# ./a.sh ./a.sh [root@ubuntu2204 ~]# ./b.sh ./b.sh #可以用这种方法实现,根据不同路径调用,对应不同功能。比如开机,关机,重启
传参的三种方法
1)直接传参 2)赋值传参 3)read传参 -t 参数 等待几秒钟输入 -p 输入提示信息 [root@shell01 ~]# vim test.sh #第一种传参方式 echo $1 $2 #第二种传参方式 name=$1 age=$2 echo "你输入的变量值为: $name $age" #第三种传参方式 read -p "请输入名字和年龄" name1 age1 echo $name1 $age1 或者 交互两次 read -p "请输入名字" name1 read -t 5 -p "请输入年龄" age1 #5秒钟不输入自动退出 echo $name1 $age1 #命令行下执行read [root@shell01 ~]# read test xxxhh [root@shell01 ~]# echo $test xxxhh read案例: 1.使用read传参的方式 更改系统的主机名称 [root@shell01 scripts]# vim read-5.sh #!/bin/bash #1.提示用户输入新的主机名 read -p "请输入你要修改的主机名: " Hostname #2.提示用户是否确认修改主机名 read -p "请确认是否真正修改主机名[yes|no]: " Qr #3.根据用户的输入进行是否修改主机名 if [ "$Qr" == "yes" ];then #字符串比较用==,字符串变量最好加"" echo "你选择修改主机名..." hostnamectl set-hostname $Hostname &>/dev/null if [ $? -eq 0 ];then echo "主机名修改成功..." #bash && source /etc/profile #bash命令进入另外一种环境,后面的命令是针对这个脚本不会执行 bash else echo "主机名修改失败..." fi else echo "你选择了不修改主机名..." echo "程序脚本退出..." fi 2.测试用户输入的IP地址是否能通 ping -c1 -W1 10.0.0.7 #-c1表示只ping一个包 -W1表示超时时间1秒 [root@shell01 scripts]# vim read-3.sh #!/bin/bash #1.提示用户输入一个IP地址 read -p "请输入一个IP地址进行测试连通情况: " Ip #2.根据用户输入的IP地址进行测试,将测试结果返回给用户 (&&左面都成功就执行 ||左面有不成功就执行) ping -c1 -W1 $Ip &>/dev/null && echo "该IP地址$Ip 是通畅的!" || echo "该IP地址$Ip 是不通的! " [root@shell01 scripts]# sh read-3.sh 请输入一个IP地址进行测试连通情况: 10.0.0.2 该IP地址10.0.0.2 是通畅的! [root@shell01 scripts]# sh read-3.sh 请输入一个IP地址进行测试连通情况: 10.0.0.7 该IP地址10.0.0.7 是不通的! echo命令特性: 输出颜色 #-e参数加上颜色或者背景色 [root@shell01 scripts]# vim read-3.sh #!/bin/bash #1.提示用户输入一个IP地址 read -p "请输入一个IP地址进行测试连通情况: " Ip #2.根据用户输入的IP地址进行测试 ping -c1 -W1 $Ip &>/dev/null #3.根据测试结果返回给用户 if [ $? -eq 0 ];then echo -e "\033[32m该IP地址$Ip 是通畅的!\033[0m" #绿色 else echo -e "\033[31m该IP地址$Ip 是不通的!\033[0m" #红色 fi 3.写个脚本,修改主机IP地址 1.修改IP地址,提示用户IP地址修改成功 2.怎么生效,重启网络服务,IP地址修改之后,远程连接会断开 3.怎么修改IP地址,10 和 172 网段都要修改 [root@shell01 scripts]# vim read-6.sh #!/bin/bash #1.提示用户输入新的IP地址的主机位 read -p "请输入要修改之后的IP地址的主机位: " Host #2.提示用户是否确认修改IP地址的主机位 read -p "是否确认修改主机的IP地址[yes|no]: " Qr #3.根据用户的输入进行判断是否修改IP地址 if [ "$Qr" == "yes" ];then echo "你选择了修改主机的IP地址..." #下面正则匹配,.*为贪婪匹配,\.用于转义识别.,\1指代前面括号内匹配的内容 sed -ri "s#(^IPADDR.*\.).*#\1$Host#g"/etc/sysconfig/network-scripts/ifcfg-eth[01] &>/dev/null if [ $? -eq 0 ];then echo "主机的IP地址修改成功!" read -p "是否确认重启网络服务进行生效! [yes|no]: " Con if [ "$Con" == "yes" ];then echo "你选择了重启网络服务生效!注意:重启之后,当前链接会断开,需要使用新的IP地址进行连接。" systemctl restart network &>/dev/null if [ $? -ne 0 ];then echo "重启网络服务失败!" fi else echo "你没有选择重启网络服务!你可以选择手动重启!" fi else echo "主机的IP地址修改失败!" fi else echo "你选择不修改IP地址,再见!" fi
变量 | 说明 |
---|---|
${#变量} | 获取变量的长度 |
${变量#匹配规则} | 从头开始匹配,最短删除 |
${变量##匹配规则} | 从头开始匹配,最长删除 |
${变量%匹配规则} | 从尾开始匹配,最短删除 |
从尾开始匹配,最长删除 | |
${变量/旧的字符串/新的字符串} | 替换变量中的旧的字符串为新的字符串,只替换第一个 |
${变量//旧的字符串/新的字符串} | 替换变量中的旧的字符串为新的字符串,替换所有 |
${变量:匹配规则:匹配规则} | 索引及切片 |
[root@shell01 scripts]# url=www.sina.com.cn #定义变量 [root@shell01 scripts]# echo $url #打印变量 www.sina.com.cn [root@shell01 scripts]# echo ${#url} #获取变量值的长度 15 [root@shell01 scripts]# echo ${url#*.} #从头匹配,最短删除,*表示任意,最短匹配到.的内容删除 sina.com.cn [root@shell01 scripts]# echo ${url##*.} #从头开始匹配,最长删除 cn [root@shell01 scripts]# echo ${url%.*} #从尾开始删,最短删除,匹配还是从左往右匹配 www.sina.com [root@shell01 ~]# echo ${url%%.*} #从尾开始删,最长删除 www [root@shell01 ~]# echo ${url/w/W} #将旧字符串替换为新字符串,只替换第一个字符串 Www.sina.com.cn [root@shell01 ~]# echo ${url//w/W} #将旧字符串替换为新字符串,替换所有 WWW.sina.com.cn [root@shell01 ~]# echo ${url/w/} #将第一个匹配到的删除 ww.sina.com.cn [root@shell01 ~]# echo ${url//w/} #将匹配到的全部删除 .sina.com.cn [root@shell01 ~]# echo ${url:5:4} #从第5列开始切,切4个 ina. [root@shell01 ~]# echo ${url:5} #删除前5列 ina.com.cn
${var: -length} #取字符串的最右侧几个字符, 注意:冒号后必须有一空白字符
${var:offset:-length} #从最左侧跳过offset字符,一直向右取到距离最右侧lengh个字符之 前的内容,即:掐头去尾
${var: -length:-offset} #先从最右侧向左取到length个字符开始,再向右取到距离最右侧 offset个字符之间的内容,注意:-length前空格,并且length必须大于offset
#其中word可以是指定的任意字符,自左而右,查找var变量所存储的字符串中,第一次出现的word,
删除字符串开头至第一次出现word字符串(含)之间的所有字符,即懒惰模式,以第一个word为界删左留右
${var#*word}
#同上,贪婪模式,不同的是,删除的是字符串开头至最后一次由word指定的字符之间的所有内容,即贪婪模 式,以最后一个word为界删左留右
${var##*word} ${var##word}
#保留文件名
[root@ubuntu2204 ~]# nginx_url=http://nginx.org/download/nginx-1.20.2.tar.gz
[root@ubuntu2204 ~]# echo ${nginx_url##*/}
nginx-1.20.2.tar.gz
#其中word可以是指定的任意字符,功能:自右而左,查找var变量所存储的字符串中,第一次出现的word,
删除字符串最后一个字符向左至第一次出现word字符串(含)之间的所有字符,即懒惰模式,以从右向左的第一 个word为界删右留左
${var%word*} ${var%word}
#同上,只不过删除字符串最右侧的字符向左至最后一次出现word字符之间的所有字符,即贪婪模式,以从右向 左的最后一个word为界删右留左
${var%%word*} ${var%%word}
root@ubuntu2204 ~]# url=http://www.magedu.com:8080
[root@ubuntu2204 ~]# echo ${url##*:}
8080
[root@ubuntu2204 ~]# echo ${url%%:*}
http
#替换查找
#查找var所表示的字符串中,行首被pattern所匹配到的字符串,以substr替换之
${var/#pattern/substr}
#查找var所表示的字符串中,行尾被pattern所匹配到的字符串,以substr替换之
${var/%pattern/substr}
#查找并删除
#删除var表示的字符串中所有以pattern为行首匹配到的字符串
${var/#pattern}
#删除var所表示的字符串中所有以pattern为行尾所匹配到的字符串
${var/%pattern}
#把var中的所有小写字母转换为大写 ${var^^} #把var中的所有大写字母转换为小写 ${var,,}
例: [root@ubuntu2204 ~]# str=abcd1234ABCD12345 #所有小写转大写 [root@ubuntu2204 ~]# echo ${str^^} ABCD1234ABCD12345 #所有大写转小写 [root@ubuntu2204 ~]# echo ${str,,} abcd1234abcd12345 #tr实现大小写转换 [root@ubuntu2204 ~]# echo $str | tr 'a-z' 'A-Z' ABCD1234ABCD12345
变量扩展
#扩展以所有prefix开头的变量 ${!prefix*} ${!prefix@} 模糊匹配变量名 [root@ubuntu2204 ~]# file1=a;file2=b;file3=c [root@ubuntu2204 ~]# echo ${!file*} file1 file2 file3 [root@ubuntu2204 ~]# echo ${!file@} file1 file2 file3
1.查看当前内存的使用率,如果使用率大于80%,则进行报警。 1.怎么查看内存的使用情况 free -m 2.怎么获取已使用的内存使用率 free -m|awk '/^Mem/{print $3/$2*100}' 3.怎么进行对比,使用率大于80,获取证书 4.根据判断,返回结果,大于80则报警,正常就正常提示 [root@shell01 scripts]# vim free_use.sh #!/bin/bash #1.定义内存使用率的大小变量 Free_Used=$(free -m|awk '/^Mem/{print $3/$2*100}') #2.拿内存的使用率大小跟80进行比较 if [ ${Free_Used%.*} -ge 80 ];then echo "当前内存使用率过高! 使用率为:${Free_Used}%" else echo "当前内存使用率正常!使用率为:${Free_Used}%" fi
2.把以下的字符串中长度小于3的打印出来 When find examines or prints information a file 方法一
# -n1表示每次只传一个参数给args命令 [root@shell01 ~]# echo When find examines or prints information a file|xargs -n1|awk '{if(length<5)print}' When find or a file 方法二 [root@shell01 scripts]# cat length.sh #!/bin/bash for i in When find examines or prints information a file do [ ${#i} -lt 5 ] && echo $i done [root@shell01 scripts]# sh length.sh When find or a file
shell当前进程和子进程
[root@ubuntu2204 ~]# vim test10.sh VAR1=123 VAR2=456 echo "test" echo $BASHPID sleep 20 [root@ubuntu2204 ~]# chmod +x test10.sh #当前进程中执行 [root@ubuntu2204 ~]# echo $BASHPID 2570 #test10.sh中输出的 BASHPID 也是 2570 [root@ubuntu2204 ~]# . test10.sh test 2570 #执行完成后,当前进程中能使用 test10.sh 中定义的变量 [root@ubuntu2204 ~]# echo $VAR1 $VAR2 123 456 #进程树中查看 [root@ubuntu2204 ~]# pstree -p | grep 2570 |-sshd(1009)-+-sshd(2537)---sshd(2562)---bash(2570)---sleep(9403) #子进程中执行 [root@ubuntu2204 ~]# echo $BASHPID 2570 #进程ID不一样 [root@ubuntu2204 ~]# ./test10.sh test 9501 #子进程中的变量己经销毁了 [root@ubuntu2204 ~]# echo $VAR1 $VAR2
#进程树中查看 [root@ubuntu2204 ~]# pstree -p | grep 9501 |-sshd(1009)-+-sshd(2537)---sshd(2562)---bash(2570)--- test10.sh(9501)---sleep(9502)
管道符左右两边都是子进程
#没有开启子进程 [root@ubuntu2204 ~]# echo $BASHPID;{ echo $BASHPID;sleep 100; } 2679 2679 #第一个是当前进程;第二个是第一个的子进程|第三个是第一个的子进程 (第二个和第三个是并列关系) [root@ubuntu 1]#echo $BASHPID; { echo $BASHPID; } | { xargs echo; echo $BASHPID; } 1197 1415 1416 [root@ubuntu 1]#echo $BASHPID 1197
退出状态码变量
用户可以在脚本中使用以下命令自定义退出状态码
exit [n]
注意:
- 脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字
- 如果exit后面无数字,终止退出状态取决于exit命令前面命令执行结果
- 如果没有exit命令,即未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一 条命令的状态码
脚本安全和set
set 命令:可以用来定制 shell 环境
#脚本里加入,有变量不存在,不往下执行 set -u #报错不往下执行 set -e #限制使用没声明的变量 [root@ubuntu2204 ~]# cat set2.sh #!/bin/bash set -u DIR=/test rm -rf ${DIr}/* #这个变量其实不存在,如果没有 set -u 选项,则会删除根目录 rm -rf /* [root@ubuntu2204 ~]# bash set2.sh set2.sh: line 17: DIr: unbound variable #遇到错误终止 [root@ubuntu2204 ~]# vim set3.sh #!/bin/bash set -e echo 123 cd /test2/ #这个目录不存在,如果没有 set -e,则也会删当前目录 rm -rf * echo 456 #遇到错误行就终止 [root@ubuntu2204 ~]# bash set3.sh 123 set3.sh: line 17: cd: /test2/: No such file or directory
高级变量用法-有类型变量
Shell变量一般是无类型的,但是bash Shell提供了declare和typeset两个命令用于指定变量的类型,两 个命令是等价的 格式: declare [-aAfFgilnrtux] [-p] [name[=value] ...] #选项: -f #显示已定义的所有函数名及其内容 -F #仅显示已定义的所有函数名 -p #显示每个变量的属性和值 -a #声明或显示定义为数组的变量 -A #将变量定义为关联数组 -i #声明或显示定义为整型的变量 -l #声明或显示定义为小写的变量 -n #变量引用另外一个变量的值 -r #声明或显示只读变量 -t #声明或显示具有trace(追踪)属性的变量 -u #声明或显示定义为大写的变量 -x #显示环境变量和函数,相当于export #显示所有己定义的函数和函数体 [root@ubuntu2204 ~]# declare -f #变量引用(被引用的值变了,引用的值也会变) [root@ubuntu2204 ~]# str1=1234 [root@ubuntu2204 ~]# declare -n str2=str1 [root@ubuntu2204 ~]# echo $str1 $str2 1234 1234 #str2 会跟着改变 [root@ubuntu2204 ~]# str1=abcd [root@ubuntu2204 ~]# echo $str1 $str2 abcd abcd #str1 也会跟着改变 [root@ubuntu2204 ~]# str2=xyz [root@ubuntu2204 ~]# echo $str1 $str2 xyz xyz
变量间接引用
eval命令
eval命令将会首先扫描命令行进行所有的置换,然后再执行该命令。该命令适用于那些一次扫描无法实 现其功能的变量,该命令对变量进行两次扫描
[root@ubuntu2204 ~]# CMD=whoami [root@ubuntu2204 ~]# echo $CMD whoami [root@ubuntu2204 ~]# eval $CMD root [root@ubuntu2204 ~]# n1=6 [root@ubuntu2204 ~]# echo {1..$n1} {1..6} #两次展开 [root@ubuntu2204 ~]# eval echo {1..$n1} 1 2 3 4 5 6 [root@ubuntu2204 ~]# for i in `eval echo {1..$n1}`;do echo i=$i;done i=1 i=2 i=3 i=4 i=5 i=6 [root@ubuntu2204 ~]# a=aa;b=bb [root@ubuntu2204 ~]# $a$b=cc bash: aabb=cc: command not found... [root@ubuntu2204 ~]# eval $a$b=cc [root@ubuntu2204 ~]# echo $a$b aabb [root@ubuntu2204 ~]# echo $aabb cc
间接变量引用 (了解)
如果第一个变量的值是第二个变量的名字,从第一个变量引用第二个变量的值就称为间接变量引用
variable1的值是variable2,而variable2又是变量名,variable2的值为value,间接变量引用是指通过
variable1获得变量值value的行为
variable1=variable2 variable2=value 范例: [root@ubuntu2204 ~]# var1=str [root@ubuntu2204 ~]# str=abcd [root@ubuntu2204 ~]# echo $var1 str [root@ubuntu2204 ~]# echo \$$var1 $str [root@ubuntu2204 ~]# eval echo \$$var1 abcd