• shell编程基础


    shell脚本编程基础

    【1】、shell概述

    • shell是一门编程语言,如:C、C++、java、PHP、python Go等

    • 语言分类

      编程型语言:C、C++、Go等为编译型语言。程序运行需要提前编译,编译语言都有编译器

      解释型语言:shell、PHP、python等为解释型语言,程序在运行时不需要提前编译,一边执行,一边解释,每种解释型语言都有解释器

    • shell语言支持大部分编程语言都具备的功能:if判断、for循环、函数、数组、加减乘除、逻辑运算等

    • 脚本的本质就是命令的堆积

    【2】、shell编写规范

    系统解释器

    [root@xu script]# cat /etc/shells
    /bin/sh		#Unix解释器
    /bin/bash	#Linux默认解释器
    /usr/bin/sh
    /usr/bin/bash
    

    这就是一个简单的shell script,在我们编写完成后,要给script加上可执行的权限

    #!/bin/bash
    #首先要进行环境指定,指定编译器,一般我们会使用/bin/bash,标准解释器
    # '#'表示注释、‘#!’表示指定解释器
    
    #功能:输出hello world
    echo hello world
    

    在脚本中不能出现交互式命令

    #例如创建用户并且给用户设置密码
    #!/bin/bash
    #creat user
    useradd zhanghaowei
    echo 123 | passwd --stdin zhanghaowei
    #我们使用设置密码的方式采用非交互式的方式
    

    【3】、如何写好一个shell脚本

    • 明确任务需求
    • 按照需求整理好每一个步骤,先后顺序
    • 运行脚本,并根据运行结果排除错误
    • 优化脚本并达到最后的结果
    • 脚本程序规范
      • 声明解释器(shebang)

        • !/bin/bash

      • 注释

        • 对脚本的描述
      • 执行指令

        • Linux命令

    【4】、编写脚本

    • 自动配置本地yum仓库的脚本

      [BaseOS]
      name=local_BaseOS
      baseurl=file:///mnt/cdrom/BaseOS
      enable=1
      gpgcheck=0
      
      [AppStream]
      name=local_AppStream
      baseurl=file:///mnt/cdrom/AppStream
      enable=1
      gpgcheck=0
      EOF
      
      sleep 2
      echo "======永久挂载光驱======"
      echo "/dev/cdrom        /mnt/cdrom      iso9660 defaults        0 0" >> /etc/fstab
      
      sleep 2
      echo "======检查软件包数量======"
      yum list | wc -l
      

    【5】、脚本的执行方式

    • 方式一:赋予脚本执行权限后,可用绝对路径和相对路径执行

    • 方式二:调用解释器执行脚本文件,会开启jie'shi'q

    • 方式三:source和 . :执行脚本文件

    • #绝对路径执行脚本
      [root@xu script]# /script/hello.sh 
      hello world
      #相对路径执行脚本
      [root@xu script]# ./hello.sh 
      hello world
      
    • #调用解释器执行脚本,调用解释器时可以不给执行权限
      [root@xu script]# bash /script/hello.sh 
      hello world
      
    • 父子shell问题

      #我们知道脚本的执行是通过调用bash解释器去执行,我们通过sshd连接一个会话可以存在多个bash进程
      ─sshd(1065)───sshd(1896)───sshd(1900)───bash(1901)───bash(2093)───bash(2114)───pstree(2135)
      #通过pstree我们可以看到当前session存在多个bash环境,都是由systemd进程创建出来的,而我们现在处于bash(2114)环境中,如果此时我们执行脚本采用的环境时bash(2114)环境。
      #如果我们使用路径(source)去执行脚本和使用bash去执行脚本就会产生区别
      #采用.(source)去执行脚本,是在当前的bash环境下执行的,而使用bash或path去执行脚本则是在当前的bash环境下创建出一个子bash去执行脚本,并切换当前环境为新的脚本
      

    【6】、变量

    • shell中变量类型只有string,没有别的;比方说在python中变量是有不同的类型的(list、dic、str。。。),但是在shell中只有str.

    • 变量的存在让脚本变得更加灵活,能够应对不同的场景

    • 语法:变量名="变量值"

    • $:调用变量

      [root@xu ~]# name=$(hostname)
      [root@xu ~]# echo $name
      xu
      

    1、变量分类

    (1)、系统定义变量

    # 系统自定义的系统变量
    [root@moudle01 17:23:53  /script]# echo $PWD
    /script
    [root@moudle01 17:23:59  /script]# echo $SHELL
    /bin/bash
    [root@moudle01 17:24:05  /script]# echo $HOME
    /root
    [root@moudle01 17:24:14  /script]# echo $USER
    root
    [root@moudle01 17:24:51  /script]# echo $UID
    0
    

    (2)、位置变量和预定义变量

    在shell中也存在着和C语言相似的参数传递问题

    只不过在shell中的参数问题是通过命令去完成的,我们在执行一个shell scrip时可以写上参数,将参数传递进shell script,同时在shell中去接收通过命令传入的参数。

    位置参数:在执行脚本的命令中写上的参数就是位置参数,从1开始

    在接收时可以使用以下标识符

    • $0:提取文件名(用source执行脚本时,不显示文件名而是解释器名字)
    • ${1}:提取位置参数为1的值
    • ...
    • ${20}: 提取位置参数为20的值
    • $*:接收所有参数
    • $@:接收所有参数
    • $$:提取当前进程的PId
    • $?:上一条命令的执行结果(上一个程序/命令的返回状态码)
    • $#:所有参数个数
    #!/bin/bash
    echo $1
    echo $2
    echo $3
    echo $0
    echo $*
    echo $#
    echo $$
    echo $?
    [root@moudle01 21:10:40  /script]# bash test04.sh  a b c d
    a
    b
    c
    test04.sh
    a b c d
    4
    2728
    0
    
    [root@moudle01 21:18:33  /script]# kjasdhfsdlgasgasgadfg
    -bash: kjasdhfsdlgasgasgadfg: command not found
    [root@moudle01 21:18:45  /script]# echo $?
    127
    [root@moudle01 21:18:49  /script]# echo $?
    0
    

    创建用户tom,密码123

    #!/bin/bash
      
    useradd $1
    echo $2  | passwd --stdin $1
    

    2、env和set

    env:所有环境变量

    set:所有变量

    [root@moudle01 21:30:10  /script]# abcd=6666666
    [root@moudle01 21:30:55  /script]# set | grep abcd
    abcd=6666666
    [root@moudle01 21:31:00  /script]# env | grep PATH
    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
    
    

    3、常用的特殊符号

    • “”:双引号,引用整体,界定范围

    • ‘’:单引号,引用整体,并且取消所有特殊字符含义,界定范围

      [root@moudle01 21:32:13  /script]# a="  a"
      [root@moudle01 21:36:17  /script]# echo "$a"
        a
      # 如果有特殊符号,定义时单引号和双引号都行,在引用时只能使用双引号
        [root@moudle01 21:36:25  /script]# echo '$a'
      $a
      
      
    • $[]、$(())、expr:四则运算

      [root@xu script]# echo $[1+1]
      2
      [root@xu script]# echo $[2*3]
      6
      [root@xu script]# echo $[2-3]
      -1
      [root@xu script]# echo $[6/3]
      2
      [root@xu script]# echo $[10%3]
      1
      # expr 运算符号两侧必须加空格
      [root@moudle01 16:13:56  ~]# expr 1 + 1
      2
      
    • $():取命令结果作为参数

    • ``:反撇号,将命令的输出结果作为参数

      [root@moudle01 21:40:57  /script]# date
      Wed Jun 12 21:41:07 CST 2024
      [root@moudle01 21:38:26  /script]# a=`date`
      [root@moudle01 21:40:54  /script]# echo $a
      Wed Jun 12 21:40:54 CST 2024
      [root@moudle01 21:41:07  /script]# x=$(ls)
      [root@moudle01 21:42:37  /script]# echo $x
      abc rsync_inotify.sh test01.sh test02.sh test03.sh test04.sh test05.sh
      

    4、read

    交互式输入变量

    [root@moudle01 21:48:04  /script]# read -p "请输入:" a
    请输入:123
    [root@moudle01 21:48:47  /script]# echo $a
    123
    
    #!/bin/bash
    read -p "请输入要创建的用户名:" a
    useradd $a
    read -p "请输入用户的密码:" p
    echo $p  | passwd --stdin $a
    
    [root@moudle01 21:50:41  /script]# source test05.sh 
    请输入要创建的用户名:xxx
    请输入用户的密码:123
    Changing password for user xxx.
    passwd: all authentication tokens updated successfully
    

    5、屏蔽回显

    在敲完之后,你在后面输入的所有内容都看不到

    [root@moudle01 21:51:16  /script]# stty -echo
    

    显示回显

    [root@moudle01 21:51:16  /script]# stty echo
    

    写入创建用户的脚本,让密码输入更加安全

    #!/bin/bash
    read -p "请输入要创建的用户名:" a
    useradd $a
    stty -echo
    read -p "请输入用户的密码:" p
    stty echo
    echo ""
    echo $p  | passwd --stdin $a
    
    [root@moudle01 21:59:24  /script]# source test05.sh 
    请输入要创建的用户名:aaa
    请输入用户的密码:
    Changing password for user aaa.
    passwd: all authentication tokens updated successfully.
    

    6、export命令

    • 环境变量(全局变量):系统在全局生效的变量

      普通变量(局部变量):只针对局部环境生效,如当前某一个login.bash脚本生效

      全局变量和局部变量就是相对于父子shell(bash)。环境变量(全集变量)就是父shell也可以使用,普通变量(局部变量)只有子shell可以使用

    • 变量生存周期

      1、永久变量:写入文件,反复读取、加载,让其永久生效,如/etc/profile的PATH修改

      2、临时变量:如命令行export定义的一个变量存储数据,关闭shell后失效

    • 在定义变量时有两种选择

    • 1、加export:在父shell中定义的变量可以在子shell中去使用,也就是全局变量

      • [root@xu ~]# export name="xuruizhao"
        [root@xu ~]# echo ${name}
        xuruizhao
        [root@xu ~]# bash
        Skill is acquired through repeated practice, and practice makes perfect.
        [root@xu ~]# bash
        Skill is acquired through repeated practice, and practice makes perfect.
        [root@xu ~]# echo ${name}
        xuruizhao
        

      2、不加export:只对当前的shell有效,子shell看不到,也就是局部变量

      • [root@xu ~]# name="hello world"
        [root@xu ~]# echo ${name}
        hello world
        [root@xu ~]# bash
        #我们切换了bash环境,也就是进入了子shell,由于没有加export子shell中是不能使用父shell中定义的变量
        Skill is acquired through repeated practice, and practice makes perfect.
        [root@xu ~]# echo ${name}
                                
        [root@xu ~]# 
        

    7、let定义变量

    使用let定义变量,可以在定义时进行计算

    [root@moudle01 22:13:42  /script]# let a=1+1
    [root@moudle01 22:14:21  /script]# echo $a
    2
    [root@moudle01 22:16:30  /script]# let a+=3
    [root@moudle01 22:16:35  /script]# echo $a
    5
    

    8、bc命令

    上面的运算均不支持小数

    bc命令支持小数

    [root@moudle01 16:13:58  ~]# echo "1.1+1" | bc
    2.1
    

    9、变量定义初值

    #!/bin/bash
      
    read -p "请输入用户名" username
    if [ -z $username ];then
        echo "用户名不能为空" && exit
    fi
    useradd $username
    read -p "请输入密码" pass
    echo ${pass:-123456} | passwd --stdin $username
    [root@moudle01 11:37:41  /script]# bash user_add02.sh 
    请输入用户名
    用户名不能为空
    

    【7】、变量测试

    两种测试格式

    test

    []

    1、字符串

    (1)、数组测试

    == 判断是否相同

    ! = 判断两侧是否不等

    [root@moudle01 16:29:46  ~]# a=1
    [root@moudle01 16:29:51  ~]# b=2
    [root@moudle01 16:29:52  ~]# [ $a != $b ]
    [root@moudle01 16:30:04  ~]# echo $?
    0
    

    如果一个变量值为空,我们在进行比较时,

    1、可以加上双引号

    2、-z:判断变量是否为空

    ​ ! -Z/-n:判断变量是否非空

     
    [root@moudle01 16:33:11  ~]# [ a == "$c" ]
    [root@moudle01 16:33:23  ~]# echo $?
    1
    [root@moudle01 16:33:27  ~]# [ -z "$c" ]
    [root@moudle01 16:35:13  ~]# echo $?
    0
    [root@moudle01 16:35:16  ~]# [ -z "$a" ]
    [root@moudle01 16:35:22  ~]# echo $?
    1
    [root@moudle01 16:38:16  ~]# c=123
    [root@moudle01 16:38:23  ~]# [ -n "$c" ] 
    [root@moudle01 16:38:26  ~]# echo $?
    0
    
    

    (2)、逻辑测试

    &&与

    ||或

    #!/bin/bash
      
    [ $USER == root ] || echo "非管理员禁止执行该脚本" && exit
    yum -y install vsftpd > /dev/null
    systemctl start vsftpd
    systemctl enable vsftpd
    # 只有root用户可以执行此脚本
    #!/bin/bash
      
    [ $USER != root ] && echo "非管理员禁止执行" && exit2
    yum -y install vsftpd > /dev/null
    systemctl start vsftpd
    systemctl enable vsftpd
    
    [root@moudle01 16:57:25  /script]# ls a ||  ls b && ls c || ls && ls /etc/yum.repos.d/
    a
    c
    bak  epel  local_yum_house.repo
    

    2、数字

    -eq:相等

    -ne:不相等

    -gt:大于

    -ge:小于等于

    -lt:小于

    -le:小于等于

    #!/bin/bash
      
    [ $# -ne 2 ] && echo "输入变量数量错误" && exit
    useradd $1
    echo $2  | passwd --stdin $1
    

    3、文件

    -e:判断文件是否存在,不关心文件类型

    -f:判断文件是否存在,必须是普通文件

    -d:判断文件是否存在,必须是目录

    [root@moudle01 17:19:49  /script]# [ -e a ]
    [root@moudle01 17:20:00  /script]# echo $?
    0
    [root@moudle01 17:20:03  /script]# [ -d a ]
    [root@moudle01 17:20:09  /script]# echo $?
    1
    [root@moudle01 17:20:10  /script]# [ -f x ]
    [root@moudle01 17:20:21  /script]# echo $?
    1
    

    -r:判断当前用户对文件是否有读权限(对root无效,只针对普通用户)

    -w:判断当前用户对文件是否有写权限(对root无效,只针对普通用户)

    -x:判断当前用户对文件是否有x权限

    [root@moudle01 17:21:45  /script]# [ -r a ]
    [root@moudle01 17:22:02  /script]# echo $?
    0
    [root@moudle01 17:22:05  /script]# [ -w a ]
    [root@moudle01 17:22:09  /script]# echo $?
    0
    [root@moudle01 17:22:10  /script]# [ -x a ]
    [root@moudle01 17:22:17  /script]# echo $?
    1
    

    4、编写脚本

    每隔两分钟检查服务器的账户数量,如果发现增加,则给管理员发邮件

    #!/bin/bash
      
    num=$(cat /etc/passwd | wc -l)
    [ num -gt 38 ] && echo "用户增加" | mail -s "user_info" root
    
    # 编写计划任务
    */2 * * * * /script/test02.sh
    

    __EOF__

  • 本文作者: 我亦无他,为手熟尔
  • 本文链接: https://www.cnblogs.com/xuruizhao/p/18295168
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    linux内核定时器的使用
    mysql数据库中的数据如何加密呢?mysql8.0自带新特性
    移动电摇小子
    学习day59
    猿创征文 |【C++】动态内存的分配与回收
    面试现场!月薪3w+的这些数据挖掘SQL面试题你都掌握了吗? ⛵
    一篇文章熟练掌握Git
    冒泡排序和快速排序
    【C++实现】 数据库连接池
    4.正则提取html中的img标签的src内容
  • 原文地址:https://www.cnblogs.com/xuruizhao/p/18295168