跳转至

shell基础

shell介绍

shell是一门动态、弱类型、解释型的语言

计算机体系架构

命令
shell解释器(进一步封装成命令)
系统调用接口(是对OS内核的封装)
OS内核
硬件

shell脚本运行步骤

前提: 当前用户需要对脚本文件拥有r和x权限
执行的两种方式: 
    在a.sh文件所在目录下 --  `bash a.sh` 
    任一目录下 --  `/root/a.sh`

现在用的shell解释器都是Bash,一个shell脚本运行的三个步骤: 
  1> 先启动bash解释器
  2> bash解释器会把a.sh的内容从硬盘读入内存
  3> bash解释器后识别刚刚读入内存中的内容,并逐行解释执行shell代码.
  Ps: 像同为解释型语言的python,底层并发的特性、字符编码的问题都与这三个步骤有关.

变量

定义变量:     height=172
访问变量:     echo $height echo ${height} (推荐)
修改变量:     height=180

删除变量:     unset height

变量值的类型: 整型、浮点型、字符串 用来写shell脚本,这三儿就够用了..

引号对变量的影响
     双引号、单引号: 都可用于定义字符串类型的变量.. 在python里没区别,在shell里单引号定义的叫做硬引用..
                  硬引用是指将引号包裹的特殊字符都当作是普通字符.. x="\$123" 等同于 y='$123'
     反引号: 取某条命令的运行结果 touch `date "+%F"`.txt 等同于 touch $(date "+%F").txt
                  反引号不能嵌套, $()可以...

系统变量:
        PS1 指的就是[root@localhost ~]
        HOSTNAME 内置主机名的名字
        USER 当前登录的用户名
        ... ... ...

"""
shell里定义变量,赋值符号“=”两边不能有空格;python建议有空格Hhh 
"""
[root@localhost ~]# height=172      ## -- 定义
[root@localhost ~]# echo $height    ## -- 访问/取值
172
[root@localhost ~]# height=180      ## -- 修改
[root@localhost ~]# echo $height
180
[root@localhost ~]# echo ${height}cm        ## -- 建议用{}将要访问的变量包裹起来
180cm

[root@localhost ~]# touch `date "+%F"`.txt
[root@localhost ~]# ll 2022-08-29.txt 
-rw-r--r-- 1 root root 0 8月  29 20:55 2022-08-29.txt

[root@localhost ~]# touch $(date "+%F").bak
[root@localhost ~]# ll 2022-08-29.bak 
-rw-r--r-- 1 root root 0 8月  29 20:57 2022-08-29.bak

变量作用域

变量作用域即变量的生效范围

全局变量

全局变量只在当前shell进程(不包含该进程的子进程)里生效!!!

[root@localhost ~]# x=1000
[root@localhost ~]# echo $x
1000
[root@localhost ~]# echo $y                 ## -- bash进程中一开始是没有变量y的存在的

[root@localhost ~]# vim b.sh
[root@localhost ~]# cat b.sh
echo $x
y = 10000
[root@localhost ~]# bash b.sh         ## -- 起了个新的bash进程,文件里面的代码在新终端里运行

[root@localhost ~]# chmod u+x b.sh 
[root@localhost ~]# ./b.sh                  ## -- 开起了一个子shell进程,在新的进程里执行的

[root@localhost ~]# source b.sh     ## -- source相当于将文件中的两行代码拿出来在当前终端运行
1000
[root@localhost ~]# echo $y                 ## -- so,可以访问到y的值
10000
[root@localhost ~]# bash                        ## -- 进入一个新终端
[root@localhost ~]# echo $y

[root@localhost ~]# exit
exit
[root@localhost ~]# echo $y
10000

export

通过export 变量 ,可以将此变量继承到子子孙孙...
跟 9_权限管理之su与sudo.md中 配置文件的妙用 那部分的知识点可以呼应起来..

"""
x变量
第一层 111
第二层 222
第三层 333
  有就用自己的,没有则继承最近的父类的x值;
  exit退出本层到上一层,查看x变量的值,也是以本层有没有,没有再往上层看的逻辑查看..
  不会说,第三层的x值变为333,第一层第二层的值都变为了333..
"""
[root@localhost ~]# x=111
[root@localhost ~]# export x
[root@localhost ~]# bash
[root@localhost ~]# echo $x
111
[root@localhost ~]# x=222
[root@localhost ~]# bash
[root@localhost ~]# echo $x
222
[root@localhost ~]# x=333
[root@localhost ~]# exit
exit
[root@localhost ~]# echo $x
222
[root@localhost ~]# exit
exit
[root@localhost ~]# echo $x
111

元字符

\([ ]、\)(( ))

$[ ] 等同于 $(( )) 只支持整数的加减乘除取余+-*/%运算,不能做浮点数的

x=10
y=20
z=$[ $x + $y ]      z=$(( $x + $y )) 
z=$[ $x / $y ]      z=$(( $x + $y ))        ## -- 值为0
z=$[ 10 + 20 ]      z=$(( 10 + 20 ))        ## -- 可以直接写值

bc软件包

"""
浮点数的运算需要借助bc软件包
yum install bc -y
"""
## -- scale指定保留几位小数 不会四舍五入
[root@localhost ~]# echo "scale=2;10/3"|bc
3.33
## -- 有个弊端,.33在做运算时,不会识别成0.33
[root@localhost ~]# res=`echo "scale=2;1/3"|bc`
[root@localhost ~]# echo $res
.33
[root@localhost ~]# echo $[ $res * 100 ]
-bash: .33 * 100 : 语法错误: 期待操作数 (错误符号是 ".33 * 100 "## -- 解决: 借助cut做分割处理
## echo $(echo "scale=2;1/3"|bc|cut -d. -f2)%
[root@localhost ~]# echo `echo "scale=2;1/3"|bc|cut -d. -f2`%
33%

练习:计算内存的可用率(百分比)

[root@localhost ~]# free
              total        used        free      shared  buff/cache   available
Mem:         995640      169672      522184        8192      303784      676212
Swap:       2097148           0     2097148
# awk默认空格分割,NR指定第2行
[root@localhost ~]# avail=$(free | awk 'NR==2{print $7}')
[root@localhost ~]# total=$(free | awk 'NR==2{print $2}')
[root@localhost ~]# echo $avail $total
675564 995640
[root@localhost ~]# per=`echo "scale=2;$avail/$total"|bc|cut -d. -f2`
[root@localhost ~]# echo ${per}%
67%

其他元字符

sed、grep、awk文件处理三剑客这三个命令工具是支持正则表达式的使用的!!
其余的命令暂且只能使用元字符!

元字符 含义
~ 家目录
`` 取命令运行结果,不可嵌套
$() 取命令运行结果,可以嵌套
$(()) 或者 $[] 里面可以做整数运算
${} 界定边界,包含
touch {1..6}{d,e,f}.txt 6*3=18种可能
$? 上一条命令是否运行成功,值为0成功,非0失败..
() 括号里写命令,代表在子shell中提交任务
代表任意一个字符,不能指定 ll /test/?.txt
[] 取其中任一一个字符
也可以做测试用(结合$?使用), test命令
! 或者 ^ 取反,在[]号里使用
- 代表范围
& 放在命令末尾意味着命令后台运行;
&& 并且 类似于and 同真为真(找错的 遇错则停)
|| 或者 类似于or 有真为真(找对的 遇对则停)
; 从左到右依次执行,哪怕中途失败,后面的照常运行
* 代表所有
= 赋值
== 判断值是否相等
\ 转义
: 这也是个命令,跟true一样 运行结果永远为真
[!12][^21].txt      ## -- 文件名由两个字符组成,第一个字符不是1和2,第二个字符不是2和1,并以.txt结尾的文件
[0-9a-z].txt        ## -- 0-9,a-z中任一字符
echooo 123 && pwd   ## &&左边命令出错,&&右边命令是不会运行的
echooo 123 || pwd   ## ||左边命令失败,&&右边命令是会运行的

[root@localhost ~]# :
[root@localhost ~]# echo $?
0
[root@localhost ~]# true
[root@localhost ~]# echo $?
0

条件测试

▲ 字符串的判断
## -- [ "egon" = "123" ] 注意:错了也不会有提示信息的 
[root@localhost ~]# name="egon"
[root@localhost ~]# [ $name = "egon111" ]           ## -- 记得里面的空格
[root@localhost ~]# echo $?                                     ## -- $?表明上一条命令是否运行成功   0为成功,非0失败
1
[root@localhost ~]# [ $name = "egon" ]
[root@localhost ~]# echo $?
0
[root@localhost ~]# [ $name != "egon111" ]
[root@localhost ~]# echo $?
0
[root@localhost ~]# [ "aa" != "aa" ] && echo "ok" || echo "no"
no
[root@localhost ~]# [ "aa" = "aa" ] && echo "ok" || echo "no"
ok


▲ 数字的判断 
  -eq 等于
  -gt 大于>
  -ge   大于等于>=
  -lt   小于
  -le   小于等于 
  -ne 不等于
[root@localhost ~]# [ 3 -lt 5 ]
[root@localhost ~]# echo $?
0

▲ 文件的判断 
  -f 存在并且得是个标准文件
    -e 文件存在,不管类型

[root@localhost ~]# [ -f /root/a.sh ]
[root@localhost ~]# echo $?
0

练习:登录脚本

[root@localhost ~]# vim login.sh
[root@localhost ~]# cat login.sh
#!/bin/bash

read -p "请输入您的用户名:" username
read -p "请输入您的密码:" password

## -- 对 && 运行  ;  错 && 不运行  ;  对 || 不运行  ;  错 || 运行     
[ $username = "egon" ] && [ $password = 123 ] && echo "认证成功"  || echo "认证失败"
[root@localhost ~]# bash login.sh 
请输入您的用户名:egon
请输入您的密码:123
认证成功
[root@localhost ~]# bash login.sh 
请输入您的用户名:dc
请输入您的密码:123
认证失败

流程控制

if判断

命令1
命令2
if 条件1;then
     代码 elif 条件2;then
     代码 elif 条件3;then
     代码
else
     代码
fi
命令3

[root@localhost ~]# vim if1.sh
[root@localhost ~]# cat if1.sh
#!/bin/bash


# -- 单分支
if [ 10 -gt 3 ];then
    echo "10>3? yes"
fi

# -- 双分支
if [ 3 -gt 10 ];then
    echo "ok"
else
    echo "3>10? no"
fi

# -- 多分支
read -p "请输入您的成绩: " score

if [ $score -ge 90 ];then
    echo "优秀"
# -- 可以优化判断条件 能走到这一步 score值肯定是小于90的 elif [ $score -ge 80 ];then 
elif [ $score -ge 80 ] && [ $score -lt 90 ];then
    echo "良好"
elif [ $score -ge 70 ] && [ $score -lt 80 ];then
    echo "一般"
else
    echo "很差"
fi  
[root@localhost ~]# bash if1.sh 
10>3? yes
3>10? no
请输入您的成绩: 76  
一般

while循环

[root@localhost ~]# vim while.sh 
[root@localhost ~]# bash while.sh 
请输入您的用户名:dc
请输入您的密码:123456
认证失败
请输入您的用户名:egon
请输入您的密码:123
认证成功
[root@localhost ~]# cat while.sh 
#!/bin/bash

while true
do
    read -p "请输入您的用户名:" username
    read -p "请输入您的密码:" password

    if [ $username = "egon" ] && [ $password = 123 ];then
        echo "认证成功"
    break
    else
        echo "认证失败"
    fi
done


[root@localhost ~]# vim while2.sh
[root@localhost ~]# bash while2.sh
0
1
2
[root@localhost ~]# cat while2.sh
#!/bin/bash

count=0

while [ $count -lt 3 ];do
    echo $count
    ## -- 等同于 count = $[ $count + 1 ]
    ((count++))
done

Ps: shell脚本的缩进 control +v 上下选择缩进行 shift+i 四个空格先缩进第一行 两下esc实现选择的行全部缩进..

for循环

[root@localhost ~]# seq 1 3
1
2
3

[root@localhost ~]# vim for.sh
[root@localhost ~]# cat for.sh
#!/bin/bash

# -- 以空格为分隔符,依次读1 2 3 4 5赋值给变量i
# -- 等同于 for i in `seq 1 5`
# -- 等同于 for i in {1..5}
for i in 1 2 3 4 5
do
    # -- 当i的值为4时,通过break跳出循环
    if [ $i -eq 4 ];then
        break
    fi
    echo $i
done


for i in `ls /root | grep .sh`
do
    echo $i
done
[root@localhost ~]# bash for.sh
1
2
3
for.sh

练习

案例1: 创建check_network.sh检测是否能连通某个IP

"""
#!/bin/bash

ping -c 5  www.baidu.com &> /dev/null

if [ $? -eq 0 ];then
    echo "network ok"
else
    echo "network error"
fi
"""

[root@localhost ~]# vim check_network.sh
[root@localhost ~]# bash check_network.sh
network ok

案例2: 检测同网段哪些IP能ping通(并发)

#!/bin/bash

for i in {3..254}
do 
    # -- 此方案,一条条的检测太慢了!!!
    # ping -c 1 172.16.150.$i &> /dev/null
    # if [ $? -eq 0 ];then
    #    echo "172.16.150.$i up" # -- 注意,若用单引号,$就没有取值之意啦
    # else
    #    echo "172.16.150.$i down"
    # fi

    # -- 瞬间200多条ping命令全部提交到后台,实现并发运行
    # -- 等同于 ping -c 1 172.16.150.$i &> /dev/null && echo "172.16.150.$i up" >> /tmp/ip.log || echo "172.16.150.$i down" >> /tmp/ip.log &

    (ping -c 1 172.16.150.$i &> /dev/null
    if [ $? -eq 0 ];then
       echo "172.16.150.$i up" >> /tmp/ip.log
    else
       echo "172.16.150.$i down" >> /tmp/ip.log
    fi) &

done