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中执行命令是没有记录的)

什么是Shell脚本

1.系统命令的堆积
2.特定的格式。特定的语法 组成的一个文件
3.以.sh为结尾的。

为什么要学习Shell脚本

自动化的运维
减少不必要的工作
提高工作的效率

学习Shell脚本,我们需要什么技能

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服务,服务配置,网络,优化,日志等的操作。

Shell脚本能干什么

1.基础配置
2.安装软件
3.配置变更
4.业务不熟 Git jenkins Shell 进行自动化代码上线
5.日常的备份  通过定时任务+脚本
6.信息收集      Zabbix + Shell  自动化的监控系统的状态
7.日志分析     Elk
8.服务扩容/缩容 Zabbix + Shell

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

配置vim自动生成注释

#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

 

Shell的执行方式

脚本从上到下,从左到右的方式执行。如果报错会继续执行下去。
脚本执行时遇到子脚本,会执行子脚本,子脚本执行完成后,退出子脚本,继续执行脚本中的内容。

脚本运行时,会向系统内核请求一个进程。脚本就会在该进程下进行运行,终止操作。

执行通过哪些方式进行执行:
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

登录式Shell和非登录式

登录式     通过用户名和密码的方式进行登录的

非登录式   不是通过用户和密码的方式进行登录的

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

 

Shell变量替换

变量说明
${#变量} 获取变量的长度
${变量#匹配规则} 从头开始匹配,最短删除
${变量##匹配规则} 从头开始匹配,最长删除
${变量%匹配规则} 从尾开始匹配,最短删除
${变量%%匹配规则} 从尾开始匹配,最长删除
${变量/旧的字符串/新的字符串} 替换变量中的旧的字符串为新的字符串,只替换第一个
${变量//旧的字符串/新的字符串} 替换变量中的旧的字符串为新的字符串,替换所有
${变量:匹配规则:匹配规则} 索引及切片
[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

 

posted @ 2024-04-22 17:47  战斗小人  阅读(34)  评论(0编辑  收藏  举报