• shell脚本详解


    当你进入Linux世界的大门时,就会遇到一个强大而又神奇的工具——Shell。Shell是一种命令行解释器,为你在Linux系统中与计算机进行互动提供了无限的可能性。

    学习Shell可以让你获得强大的自动化和脚本编程能力,让你更高效地处理文件和目录、管理进程、配置系统以及执行各种任务。不仅如此,Shell还是与服务器和云平台交互的主要方式,掌握这个技能将助你在开发和运维领域中更上一层楼。

    通过学习Shell,你将:
    - 掌握命令行界面,成为Linux系统的主人,极大提升工作效率;
    - 理解Linux系统的运行方式和机制,探索其背后的原理与逻辑;
    - 高效地处理和管理文件、目录以及系统资源;
    - 通过编写简单而又强大的Shell脚本,实现自动化的工作流程和任务;
    - 成为一名优秀的开发者或运维工程师,拥有更广阔的就业和发展机会。

    Shell学习曲线不可否认会有一些陡峭,但一旦你理解其基本概念和语法,你会发现它是如此有趣和有用。无论你是初学者还是有一定经验的用户,Shell都能够为你提供强大的工具和技能,让你成为Linux世界中的骄傲。

    所以,准备好挑战和探索吧!迈出第一步,掌握Shell,它将是你在Linux旅程中最重要的伙伴之一。开始学习Shell,让命令行的魔力展现在你的指尖吧!

     

    概述

    Shell

    简介
    Shell是一个用C语言编写的程序,它是用户使用Linux的桥梁
    Shell既是一种命令语言,又是一种程序设计语言
    Shell是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务

    优点
    Shell属于内置的脚本,程序开发的效率非常高,依赖于功能强大的命令可以迅速地完成开发任务
    语法简单,代码写起来比较轻松,简单易学
            

    Shell分类

    分类
    在linux中有很多类型的shell,不同的shell具备不同的功能,shell还决定了脚本中函数的语法
    /bin/sh
    /bin/bash【默认】
    /sbin/nologin
    /usr/bin/sh
    /usr/bin/bash
    /usr/sbin/nologin
    /bin/tcsh
    /bin/csh

    查看流行shell
    cat /etc/shells

    当前系统使用的Shell
    echo $SHELL

    基础内容

    风格

    文件命名规范
    文件名.sh .sh是linux下bash shell 的默认后缀

    Shell解析器
    指定告知系统当前这个脚本要使用的shell解释器【shell文件中的第一句】
    #!/bin/bash
              

    编写脚本

    创建一个scripts文件夹,用来存储文件
    mkdir scripts
    cd scripts

    编写脚本
    vim hello.sh
    #!/bin/bash
    echo "Hello World"

    执行脚本

    第一种
    bash ./hello.sh 和 sh ./hello.sh

    第二种
    source hello.sh 和 . hello.sh

    第三种
    ./hello.sh
    注意需要权限
    chmod +x hello.sh

    区别
    第一种是在子bash环境下运行,而第二种是在当前bash环境下运行
    我们通过运行type source可以看到source is a shell builtin(source是shell的一个内嵌)
    我们执行ps -f可以查看当前bash环境,创建一个子bash,继续通过ps -f查看,在当前子bash环境下,你依然可以运行shell文件
    从结果上来看,两者好像没有什么区别,但是如果引入更多知识,例如变量:如果子shell【bash】中设置的当前变量,父shell【bash】是可不见的
               

    bash环境
    查看当前bash环境
    ps -f

    创建一个新的bash环境【子bash】
    当输入bash命令并按下回车键时,系统将启动一个新的Bash Shell环境,并将当前终端切换到该环境下

    返回上级bash
    exit
              

    变量

    全局变量和局部变量

    全局变量
    层层嵌套的子bash依然可以访问

    局部变量
    只在当前的bash中可以访问,子bash和父bash都不能访问呢

    查看变量是全局还是局部
    全局:env | grep 变量名
    局部:set | grep 变量名
            

    系统预定义变量

    简介
    系统定义好的变量,可以直接拿来用

    常用系统变量
    查看当前所有的全局系统变量:env
    查看当前所有的变量(包含全局和局部的,系统的,用户的):set
    $HOME 、$PWD 、$SHELL 、$USER

    测试
    [root@localhost scripts]# echo $HOME

    用户自定义变量

    简介
    用户自己定义的变量

    规则
    变量名=变量值
    1.等号前后不能有空格
    2.在声明变量的时候是不需要添加$符号,但是使用时候需要添加
    3.如果定义的是一个字符串,需要将值添加双引号或者单引号

    规范
    变量命名规范:字母、数字和下划线组成,其中数字不能开头
    自定义变量一般都是小写的
    在shell中,变量是没有类型的,或者我们理解为全部都是字符串类型
    如果变量的值需要做数值运算,可以使用$((1+1))或者$[1+1]的形式

    定义局部变量
    [root@localhost scripts]# a=10
    [root@localhost scripts]# str1="test thi var"
    [root@localhost scripts]# echo $a


    定义全局变量
    [root@localhost scripts]# a=10
    [root@localhost scripts]# export a
    [root@localhost scripts]# bash
    [root@localhost scripts]# echo $a
    10
    定义一个全局变量需要先声明一个局部变量,然后再通过export导出为一个全局变量
    注意:在子bash中修改全局变量,只会再当前环境中生效,不会影响父bash环境,哪怕是你增加export也依然不会影响到父bash环境

    Shell脚本中使用变量
    我们可以在hello.sh的脚本中去调用全局和局部变量
    注意:在shell脚本中使用变量,同样遵循全局和局部变量的规则
    #!/bin/bash
    echo $txt            

    只读变量和撤销变量

    只读变量
    [root@localhost scripts]# readonly a1=10
    readonly 变量名=值
    在shell中,只读变量相当于是常量,定义之后不允许修改

    撤销变量
    [root@localhost scripts]# unset a
    变量定义之后是可以撤销的,使用unset 变量名就可以撤销了

    特殊变量

    简介
    在Shell中,存在一些特殊变量,他们具有特殊的意义

    $n
    $n代表接受参数,n是数字,代表在执行脚本时候传递的参数数量
    例如$1-$9代表第一个到第九个参数,十以上的数字,可以使用大括号包裹,例如${10}
    比较特殊的是$0,代表当前脚本名称

    $#
    $#获取输入参数的个数,一般用于循环中,判断参数的个数是否正确,加强脚本的健壮性

    $*和$@
    $*和$@非常相似,都代表命令行所有的参数
    但是$*把参数看成是一个整体,例如123 456
    而$@把每个参数区分对待,例如[123,456]
    注意:在没有循环遍历时候,两者效果一致

    $?
    $?最后一次执行命令的状态,如果是结果是0,证明上面执行的命令都是正确的
    如果结果不是0(具体是哪个数字,有命令自己决定),则证明上面命令不正确了
              

    运算符

    简介
    Shell中存在expr表达式用于做运算
    运算需要使用$((a+b))或者$[a+b]的形式

    表达式输出
    [root@localhost scripts]# expr $[10+10]
    [root@localhost scripts]# expr $((10+5))

    表达式赋值给变量
    [root@localhost scripts]# a=$(expr 10 + 5)

    脚本中的应用
    创建add.sh,在执行的时候,通过传递参数的形式实现加法效果
    #!/bin/bash
    sum=$[$1 + $2]
    echo sum=$sum          
              
            

            

    条件判断

    相等判断

    基本语法
    test 表达式
    [ 表达式 ]  注意:中括号前后需要有空格

    示例1
    注意:=前后需要有空格,否则会认为是一个整体,判断为正确,永远0
    [root@localhost ~]# test $a = 10
    [root@localhost ~]# echo $?
    0
    [root@localhost ~]# test $a = 11
    [root@localhost ~]# echo $?
    1

    示例2
    注意:记得加空格
    [root@localhost ~]# [ $a = 10 ]
    [root@localhost ~]# echo $?
    0
    [root@localhost ~]# [ $a = 11 ]
    [root@localhost ~]# echo $?
    1
    [root@localhost scripts]# [ $a != 11 ]
    [root@localhost scripts]# echo $?
    0

    两个值比较

    简介
    -eq    等于
    -ne    不等于
    -lt    小于
    -le    小于等于
    -gt    大于
    -ge    大于等于

    示例
    [root@localhost ~]# [ $a -lt 11 ]
    [root@localhost ~]# echo $?
    0          

    文件权限判断

    简介
    -r 有读的权限
    -w 有写的权限
    -x 有执行的权限

    示例
    [root@localhost scripts]# [ -r hello.sh ]
    [root@localhost scripts]# echo $?
    0
            

    文件类型判断

    简介
    -e 文件存在
    -f 文件存在并且是一个文件类型
    -d 文件存在并且是一个目录类型

    示例
    [root@localhost scripts]# [ -e hello.sh ]
    [root@localhost scripts]# echo $?
    0
            

            

    多条件判断

    简介
    && 与的关系,两者都成立
    || 或的关系,两者有一个成立

    三元运算符
    && 表示前一个条命令执行成功之后,在执行第二个条件
    || 表示前一个条命令执行失败之后,再执行第二个条件
    由此,我们可以衍生出来,类似三元运算符的形式
    示例 [ $a -eq $b ] && echo "$a=$b" || echo "$a!=$b"

    示例
    [root@localhost scripts]# a=10
    [root@localhost scripts]# b=10
    [root@localhost scripts]# [ $a -eq $b ] && [ $b -gt 5 ]
    [root@localhost scripts]# echo $?
    0        

    流程控制

    If

    命令行
    if [ $a = 10 ];then echo 'OK'; fi
    if [ $a = 10 ] && [ $b = 11 ];then echo 'ok'; fi

    命令文件
    #!/bin/bash
    if [ $a = 10]
    then
    echo "ok"
    fi

    命令文件
    #!/bin/bash
    if [ $1 -lt 18 ]
    then
    echo "未成年"
    else
    echo "成年人"
    fi

    命令文件
    #!/bin/bash
    if [ $1 -lt 18 ]
    then
    echo "未成年"
    elif [ $1 -lt 35 ]
    then
    echo "青年人"
    elif [ $1 -lt 60 ]
    then
    echo "壮年人"
    else
    echo "老年人"
    fi       

    Case

    注意事项
    case行结尾必须为单词"in",每一个模式匹配必须以右括号")"结束
    双分号 ";;" 表示命令序列结束,相当于跳出当前判断语句
    最后 "*)" 表示默认模式结尾,不符合最终的出口

    命令文件
    #!/bin/bash
    case $1 in
    1)
    echo "值为1"
    ;;
    2)
    echo "值为2"
    ;;
    3)
    echo "值为3"
    ;;
    *)
    echo "其他数字"
    ;;
    esac
              

    For

    基本语法1
    #!/bin/bash
    for (( i=0;i<=100;i++ ))
    do
    sum=$[$sum+$i]
    done
    echo $sum

    基本语法2
    #!/bin/bash
    for os in linux windows macos
    do
    echo $os
    done

    内部运算符
    在shell中,{}是内部运算符,{}表示一个序列,例如,从1写到100: {1..100}
    #!/bin/bash
    for i in {1..100}
    do
    sum=$[$sum+$i]
    done
    echo $sum

    $*和$@
    $*和$@非常相似,都代表命令行所有的参数,但是$*把参数看成是一个整体,例如123 456。
    而$@把每个参数区分对待,例如换行显示 注意:在没有循环遍历时候,两者效果一致
    #!/bin/bash
    echo '=====$*====='
    for param in "$*"
    do
    echo $param
    done
    echo '=====$@====='
    for param in "$@"
    do
    echo $param
    done            

    While

    示例1
    #!/bin/bash
    a=1
    while [ $a -le $1 ]
    do
    sum=$[$sum+$a]
    a=$[$a+1]
    done
    echo $sum

    示例2
    #!/bin/bash
    a=1
    while [ $a -le $1 ]
    do
    #    sum=$[$sum+$a]
    #    a=$[$a+1]
    let sum+=a
    let a++
    done
    echo $sum            
              

    读取控制台输入

    基本语法
    read 选项 参数
    -p:指定读取值时候的提示符
    -t:指定读取值时候的等待时间(秒) 如果不添加 -t 表示一直等待

    示例
    read -t 10 -p "请输入您的名字:" name
    echo "welcome,$name"          
            

    函数

    Shell函数

    简介
    在Shell中,函数细分为系统函数和自定义函数

    系统函数

    获取时间戳【date +%s】
    注意:这里我们使用的系统命令(或者系统函数)date +%s要进行命令替换,也就是添加$(date +%s)
    #!/bin/bash
    filename="$1_log_$(date +%s)"
    echo $filename

    basename
    basename [string/pathname][suffix]
    basename的作用是获取文件名称,它会删除所有的前缀包括最后一个"/"字符,然后将字符串显示出来
    suffix为后缀,如果suffix被指定了,basename将会pathname或string中的suffix去掉

    basename /scripts/cmd_test.sh
    basename /scripts/cmd_test.sh .sh
    basename /abc/def/cmd_test.sh
    /abc/def/cmd_test.sh .sh

    $0是获取当前名字,但是带有路径,我们只希望获取名字,所以可以通过basename 去掉路径,甚至去掉后缀
    #!/bin/bash
    echo '=====$n====='
    echo script name:$0

    #!/bin/bash
    echo '=====$n====='
    echo script name:$(basename $0 .sh)

    dirname
    dirname获取文件路径的绝对路径,从给定的包含绝对路径的文件名中去除文件名,然后返回剩余的路径

    dirname /scripts/cmd_test.sh
    dirname /abc/def/cmd_test.sh

    我们可以获取某个文件的绝对路径
    #!/bin/bash
    echo '=====$n====='
    echo script path:$(cd $(dirname $0); pwd)
    echo script name:$(basename $0 .sh)          
            

    自定义函数

    基本语法
    function 函数名(){
    // 函数体
    return 返回值
    }

    注意
    必须在调用函数之前,先声明函数
    函数返回值可以通过$?获取,但注意,$?的值范围是0~255

    实现一个两个数值相加的函数
    #!/bin/bash
    function add(){
    s=$[$1 + $2]
    echo $s
    }
    read -p "请输入第一个参数:" a
    read -p "请输入第二个参数:" b
    sum=$(add $a $b)
    echo "和:" $sum
    echo "和的平方:"$[$sum * $sum]          
            

    正则表达式

    常规匹配
    一串不包含特殊字符的正则表达式匹配它自己
    cat /etc/passwd | grep root

    特殊字符:^
    ^匹配一行的开头
    cat /etc/passwd | grep ^a

    特殊字符:$
    $匹配一行的结束
    cat /etc/passwd | grep h$

    特殊字符:.
    .匹配一个任意字符
    cat /etc/passwd | grep r.t
    cat /etc/passwd | grep r..t

    特殊字符:*
    * 不单独使用,他和上一个字符连用,表示匹配上一个字符 0 次或多次
    cat /etc/passwd | grep ro*t
    cat /etc/passwd | grep r.*t

    字符区间(中括号):[]
    [] 表示匹配某个范围内的一个字符
    [6,8]------匹配6或者8
    [0-9]------匹配一个0-9 的数字
    [0-9]*------匹配任意长度的数字字符串
    [a-z]------匹配一个 a-z 之间的字符
    [a-z]*-----匹配任意长度的字母字符串
    [a-c,e-f]---匹配 a-c 或者 e-f之间的任意字符

    cat /etc/passwd | grep r[a,b]t
    echo "23fsdfrat23f5y2t23sdf" | grep r[a,b]t
    echo "23fsdfrat23f5y2t23sdf" | grep r[ab]t
    echo "23fsdfraabat23f5y2t23sdf" | grep r[ab]*t
    cat /etc/passwd | grep r[a-z]t
    cat /etc/passwd | grep r[a-z]*t

    特殊字符:\
    \ 表示转义,并不会单独使用。由于所有特殊字符都有其特定匹配模式,
    当我们想匹配某一特殊字符本身时(例如,我想找出所有包含"$"的行),
    就会碰到困难。此时我们就要将转义字符和特殊字符连用,来表示特殊字符本身
    cat /scripts/daily_archive.sh | grep '\$'

    匹配手机号
    echo "14747696666" | grep ^1[345789][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$
    echo "14747696666" | grep -E ^1[345789][0-9]{9}$          
            

    文本处理工具

    Cut

    简介
    cut 的工作就是“剪”,具体的说就是在文件中负责剪切数据用的
    cut 命令从文件的每行剪切字节、字符和字段并将这些字节、字符和字段输出

    基本用法
    cut [选项参数] filename
    说明:默认分隔符是制表符
    -f    列号,提取第几列
    -d    分隔符,按照指定分隔符分割列,默认是制表符"\t"
    -c    按字符进行切割 后加n表示取第几列 比如 -c 1

    cut_test.txt
    高 我
    级 来
    程 学
    序 习
    员 了

    简介
    cut -d " " -f 1 cut_test.txt
    cut -d " " -f 2,3 cut_test.txt

    简介
    cat /etc/passwd | grep bash$
    cat /etc/passwd | grep bash$ | cut -d ":" -f 1,7
    cat /etc/passwd | grep bash$ | cut -d ":" -f 5-
    cat /etc/passwd | grep bash$ | cut -d ":" -f -5
    cat /etc/passwd | grep bash$ | cut -d ":" -f 5-7

    简介
    ifconfig # 获取当前的IP
    ifconfig ens33 # 获取ens33的信息
    ifconfig ens33 | grep netmask # 获取具有IP的一行
    ifconfig ens33 | grep netmask | cut -d " " -f 10 # 切分ens33的IP
    ifconfig | grep netmask | cut -d " " -f 10 # 切分所有IP          
            

    Awk

    简介
    一个强大的文本分析工具,把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行分析处理

    基本用法
    awk [选项参数] '/pattern1/{action1} /pattern2/{action2}...' filename
    pattern:表示awk 在数据中查找的内容,就是匹配模式
    action:在找到匹配内容时所执行的一系列命令
    -F    指定输入文件分隔符
    -v    赋值一个用户定义变量

    查看是否存在
    which awk # 查看是否存在awk

    示例
    1. 搜索passwd文件以root关键字开头的所有行,并输出该行的第7列
    cat /etc/passwd | grep ^root | cut -d ":" -f 7
    cat /etc/passwd | awk -F ":" '/^root/ {print $7}'

    2. 搜索passwd文件以root关键字开头的所有行,并输入该行的第1列和第7列,中间以","分割
    cat /etc/passwd | awk -F ":" '/^root/ {print $1","$7}'

    3. 只显示/etc/passwd 的第一列和第七列,以逗号分割,且在所有行前面添加列名"start"在最后一行添加"over"
    cat /etc/passwd | awk -F ":" 'BEGIN{print "start"}{print $1","$7} END{print "over"}'

    4. 将passwd文件中的用户id增加数值1并输出
    cat /etc/passwd | awk -F ":" '{print $3+1}'
    将添加的数值变成变量
    cat /etc/passwd | awk -v i=2 -F ":" '{print $3+i}'
              

    awk内置变量

    简介
    FILENAME    文件名
    NR    已读的记录数(行号)
    NF    浏览记录的域的个数(切割后,列的个数)

    示例
    1. 统计passwd文件名,每行的行号,每列的列数
    cat /etc/passwd | awk -F ":" '{print "文件名:"FILENAME " 行号:"NR " 列数:" NF }'
    awk -F ":" '{print "文件名:"FILENAME " 行号:"NR " .数" NF }' /etc/passwd

    2. 查询ifconfig命令输出结果中的空行所在行号
    ifconfig | grep -n ^$
    ifconfig | awk '/^$/ {print NR}'
    ifconfig | awk '/^$/ {print "空行:"NR}'

    3. 切割IP
    ifconfig | awk '/netmask/ {print $2}'

     

  • 相关阅读:
    如何运行一个 SSM 项目以及解决 JDBC 连接数据库遇到的问题(已解决)
    Spring Boot 常用设计模式
    深度学习编程入门deep-learning-for-image-processing-master 关于图片分类test1_official_demo的学习
    prometheus基本介绍 prometheus和zabbix的区别 grafana可视化工具
    安装webpack的完整步骤
    二十三、设计模式之组合模式![
    设计模式 代理模式
    SpringCloud: feign配置超时时间
    从0到1了解大数据可视化平台
    广州华锐视点:VR3D技术在中学物理实验中的应用
  • 原文地址:https://blog.csdn.net/m0_63040701/article/details/132770004