shell基础
# shell介绍
shell是一门动态、弱类型、解释型的语言
# 计算机体系架构
命令
shell解释器(进一步封装成命令)
系统调用接口(是对OS内核的封装)
OS内核
硬件
2
3
4
5
# 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,底层并发的特性、字符编码的问题都与这三个步骤有关.
2
3
4
5
6
7
8
9
10
# 变量
定义变量: 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 变量作用域
变量作用域即变量的生效范围
# 全局变量
全局变量只在当前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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 元字符
# (( ))
$[ ] 等同于 $(( )) 只支持整数的加减乘除取余+-*/%运算,不能做浮点数的
x=10
y=20
z=$[ $x + $y ] z=$(( $x + $y ))
z=$[ $x / $y ] z=$(( $x + $y )) ## -- 值为0
z=$[ 10 + 20 ] z=$(( 10 + 20 )) ## -- 可以直接写值
2
3
4
5
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%
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
练习:计算内存的可用率(百分比)
[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%
2
3
4
5
6
7
8
9
10
11
12
# 其他元字符
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
2
3
4
5
6
7
8
9
10
11
# 条件测试
▲ 字符串的判断
## -- [ "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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
练习:登录脚本
[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
认证失败
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 流程控制
# 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
一般
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 练习
案例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
3
4
5
6
7
8
9
10
11
12
13
14
15
案例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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23