notebook notebook
首页
  • 计算机网络
  • 计算机系统
  • 数据结构与算法
  • 计算机专业课
  • 设计模式
  • 前端 (opens new window)
  • Java 开发
  • Python 开发
  • Golang 开发
  • Git
  • 软件设计与架构
  • 大数据与分布式系统
  • 常见开发工具

    • Nginx
  • 爬虫
  • Python 数据分析
  • 数据仓库
  • 中间件

    • MySQL
    • Redis
    • Elasticsearch
    • Kafka
  • 深度学习
  • 机器学习
  • 知识图谱
  • 图神经网络
  • 应用安全
  • 渗透测试
  • Linux
  • 云原生
面试
  • 收藏
  • paper 好句
GitHub (opens new window)

学习笔记

啦啦啦,向太阳~
首页
  • 计算机网络
  • 计算机系统
  • 数据结构与算法
  • 计算机专业课
  • 设计模式
  • 前端 (opens new window)
  • Java 开发
  • Python 开发
  • Golang 开发
  • Git
  • 软件设计与架构
  • 大数据与分布式系统
  • 常见开发工具

    • Nginx
  • 爬虫
  • Python 数据分析
  • 数据仓库
  • 中间件

    • MySQL
    • Redis
    • Elasticsearch
    • Kafka
  • 深度学习
  • 机器学习
  • 知识图谱
  • 图神经网络
  • 应用安全
  • 渗透测试
  • Linux
  • 云原生
面试
  • 收藏
  • paper 好句
GitHub (opens new window)
  • Linux

    • 韩顺平 2021 Linux 课程笔记

      • 基础篇
      • 实操篇(上)
      • 实操篇(下)
      • JavaEE 与 Python 定制篇
      • 大数据定制篇 - Shell 编程
        • 1. Shell 概述
          • 1.1 为什么要学习 Shell 编程
          • 1.2 shell 是什么
        • 2. Shell 脚本的执行方式
          • 2.1 脚本格式要求
          • 2.2 编写第一个 shell 脚本
          • 2.3 脚本的常用执行方式
        • 3. shell 的变量
          • 3.1 Shell 变量介绍
          • 3.2 Shell 变量定义
          • 3.3 shell 变量的定义
          • 3.4 设置环境变量
          • 3.5 位置参数变量
          • 3.6 预定义变量
        • 4. 运算符
        • 5. 条件判断
        • 6. 流程控制
          • 6.1 if 判断
          • 6.2 case 语句
          • 6.3 for 循环
          • 6.4 while 循环
        • 7. read 读取控制台输入
        • 8. 函数
          • 8.1 系统函数
          • 8.2 自定义函数
        • 9. 综合案例
    • TLCL(The Linux Command Line)

  • 云原生

  • 运维
  • Linux
  • 韩顺平 2021 Linux 课程笔记
yubin
2022-02-15
目录

大数据定制篇 - Shell 编程

# 1. Shell 概述

# 1.1 为什么要学习 Shell 编程

  • Linux 运维工程师在进行服务器集群管理时,需要编写 Shell 程序来进行服务器管理。
  • 对于 JavaEE 和 Python 程序员来说,工作的需要,会要求你编写一些 Shell 脚本进行程序或者是服务器的维护,比如编写一个定时备份数据库的脚本。
  • 对于大数据程序员来说,需要编写 Shell 程序来管理集群。

# 1.2 shell 是什么

Shell 是一个命令行解释器,它为用户提供了一个向 Linux 内核发送请求以便运行程序的界面系统级程序,用户可以用 Shell 来启动、挂起、停止甚至是编写一些程序。

# 2. Shell 脚本的执行方式

# 2.1 脚本格式要求

脚本以 #!/bin/bash 开头,需要有可执行权限。

# 2.2 编写第一个 shell 脚本

需求:创建一个 shell 脚本,输出 hello world!

vim hello.sh
#!/bin/bash
echo "hello,world~"
1
2
3

# 2.3 脚本的常用执行方式

# 🖊 方式一:输入脚本的绝对路径或相对路径

说明:首先要赋予脚本 +x 的权限,再执行脚本

# 🖊 方式二:sh + 脚本

说明:不用赋予脚本+x 权限,直接执行即可

比如 sh hello.sh

# 3. shell 的变量

# 3.1 Shell 变量介绍

  • Shell 变量分为系统变量和用户自定义变量。
  • 系统变量:$HOME、$PWD、$SHELL、$USER 等等,比如:echo $HOME 等等。
  • 显示 shell 所有系统变量:set 指令

# 3.2 Shell 变量定义

# 基本语法:

  • 定义变量:变量名=值
    • 注意在 shell 编程中,这种等号的两边不要打空格。
  • 撤销变量:unset 变量
  • 声明静态变量:readonly 变量,注意不能 unset

# 快速入门:

#!/bin/bash

# 案例 1:定义变量 A
A=100
# 输出变量需要加上 $
echo A=$A
echo "A=$A"
# 案例 2:撤销变量 A
unset A
echo "A=$A"
# 案例 3:声明静态的变量 B=2,不能撤销
readonly B=2
echo "B=$B"
# 此时如果 unset B,执行时会报错
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 补充:shell 脚本的注释:

  • 单行注释:#
  • 多行注释:
:<<!
需要注释的第一行内容
需要注释的第二行内容
!
1
2
3
4

# 3.3 shell 变量的定义

# 定义变量的规则:

  • 变量名称可以由字母、数字和下划线组成,但是不能以数字开头。
  • 等号两侧不能有空格
  • 变量名称一般习惯为大写,这是一个规范,遵守即可

# 将命令的返回值赋给变量:

  • A=`date`,通过反引号,运行里面的命令,并把结果返回给变量 A
  • A=$(date) 等价于反引号

# 3.4 设置环境变量

# 基本语法:

  • export 变量名=变量值:将 Shell 变量输出为环境变量
  • source 配置文件:让修改后的配置文件立即生效
  • echo $变量名:查询环境变量的值

# 快速入门:

# 通过编辑 /etc/profile 文件配置 JDK 环境变量,要增加以下命令
export JAVA_HOME=/usr/local/java/jdk1.8.0_261
export PATH=$JAVA_HOME/bin:$PATH
# 保存退出 /etc/profile 文件后,执行 source /etc/profile 命令使修改后的配置文件生效
1
2
3
4

在 /etc/profile 文件中设置的变量是全局变量。而 .bashrc文件(在用户的家目录下)则只对当前用户有用。~/.bashrc、~/.bash_file 是当前用户目录下的配置信息。修改后用 source 命令更新。

# 3.5 位置参数变量

当我们执行一个 Shell 脚本时,如果希望获取到命令行的参数信息,就可以使用到位置参数变量,比如 ./myshell.sh 100 200 , 这个就是一个执行 shell 的命令行,可以在 myshell 脚本中获取到参数信息。

# 3.5.1 基本语法

  • $n:n 为数字,​$0 代表命令本身,​$1-$9 代表第一到第九个参数,十以上的参数需要用大括号,如​ ${10}
  • $*:代表命令行中所有的参数,​$* 把所有的参数看成一个整体
  • $@:代表命令行中所有的参数,不过该命令是把每个参数区分对待
  • $#:代表命令行中所有参数的个数

# 3.5.2 案例

案例:在脚本中获取到命令行的各个参数信息

#!/bin/bash
echo "0=$0 1=$1 2=$2"
echo "命令行所有传入的参数=$*"
echo "$@"
echo "参数的个数=$#"
1
2
3
4
5

# 3.6 预定义变量

Shell 设计者事先已经定义好的变量,可以直接在 Shell 脚本中使用。不常用。

# 3.6.1 基本语法

  • $$:当前进程的进程号
  • $!:后台运行的最后一个进程的进程号
  • $?:最后一次执行的命令的返回状态。如果这个变量的值为 0,证明上一个命令正确执行;如果这个变量的值为非 0(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确

# 3.6.2 案例

#!/bin/bash
echo "当前进程号=$$"
# 后台方式运行 myShell.sh
./myShell.sh &
echo "最后的的进程号=$!"
echo "执行的值=$?"
1
2
3
4
5
6

# 4. 运算符

在 Shell 中进行各种运算操作。

# 4.1 基本语法

  • $((运算式)) 或 $[运算式]
  • 或 expr m + n。注意 expr 运算符间要有空格
    • 如果希望将 expr 的结果赋给某个变量,使用反引号 `` 将它们包围起来。
  • expr \*,/, % 分别代表乘,除,取余
    • 注意在使用 expr 时乘法前面有个转义符:\*

# 4.2 案例

#!/bin/bash
# 案例1:计算(2+3)X4 的值
# 使用第一种方式
RES1=$(((2+3)*4))
echo "res1=$RES1"
# 使用第二种方式(推荐)
RES2=$[(2+3)*4]
echo "res2=$RES2"
# 使用第三种方式(较为复杂)
TEMP=`expr 2 + 3`
RES3=`expr $TEMP \* 4` 
echo "temp=$TEMP"
echo "res3=$RES3"
echo "执行的值=$?"
# 案例2:请求出命令行的两个参数[整数]的和
SUM=$[$1+$2]
echo "sum=$SUM"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 5. 条件判断

# 5.1 基本语法

[ condition ] (注意 condition 前后要有空格),非空返回 true,可使用 $? 验证(0 为 true,>1 为 false)

# 5.2 常用判断条件

1)=:字符串是否相等

2)两个整数的比较

选项 含义
-lt 小于
-le 小于等于
-eq 等于
-gt 大于
-ge 大于等于
-ne 不等于

3)按照文件权限进行判断

选项 含义
-r 有读的权限
-w 有写的权限
-x 有执行的权限

4)按照文件类型进行判断

选项 含义
-f 文件存在并且是一个常规的文件
-e 文件存在
-d 文件存在并是一个目录
  • 注意使用时,这些 - 不能丢。

# 3.8.3 案例

#!/bin/bash
# 案例1:“ok”是否等于“ok”
# 判断语句:是否 =
if [ "ok" = "ok" ]
then
      echo "equal"
fi
# 案例2:23是否大于等于22
# 判断语句:使用 -ge
if [ 23 -ge 22 ] 
then
      echo "大于"
fi
# 案例3:/root/shcode/aaa.txt 目录中的文件是否存在
# 判断语句:使用 -f
if [ -f /root/shcode/aaa.txt ]
then
      echo "存在"
fi
# 其他案例
if [ edu ]
then
      echo "hello, edu"
fi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 6. 流程控制

# 6.1 if 判断

if [ 条件判断式 ]
then
      程序
elif [ 条件判断式 ]
then
      程序
fi
1
2
3
4
5
6
7
  • 注意:[ 条件判断式 ] 中括号和条件判断式之间必须有空格,if 与 [ 之间也有空格。

For example:

#!/bin/bash
# 案例:编写一个 Shell 程序,如果输入的参数,大于等于60,则输出“及格了”,如果小于60,则输出 “不及格”
if [ $1 -ge 60 ]
then
      echo "及格了"
elif [ $1 -lt 60 ]
then
      echo "不及格"
fi
1
2
3
4
5
6
7
8
9

# 6.2 case 语句

case $变量名 in
"值 1")
      如果变量的值等于值 1,则执行程序 1
;;
"值 2")
      如果变量的值等于值 2,则执行程序 2
;;
      ...省略其他分支...
*)
      如果变量的值都不是以上的值,则执行此程序 
;;
esac
1
2
3
4
5
6
7
8
9
10
11
12

也许 Shell 程序看起来比较诡异,但当初设计者就是这么设计的,我们也只需要承认即可。

For Example:

#!/bin/bash
# 案例:当命令行参数是1时,输出“周一”,是2时,就输出“周二”,其它情况输出“other”
case $1 in
"1")
      echo "周一"
;;
"2")
      echo "周二"
;;
*)
      echo "other" 
;;
esac
1
2
3
4
5
6
7
8
9
10
11
12
13

# 6.3 for 循环

# 🖊 基本语法 1

for 变量 in 值1 值2 值3 ...
do
      程序
done
1
2
3
4

For Example:

#!/bin/bash
# 案例:打印命令行输入的参数【可以看出 $* 和 $@ 的区别】
# 注意 $* 是把输入的参数,当作一个整体,所以只会输出一行
for i in "$*"
do
      echo "num is $i"
done
# 使用 $@ 是把输入的参数,分别对待,所以有几个参数,就会输出几行
for j in "$@"
do
      echo "num is $j"
done
1
2
3
4
5
6
7
8
9
10
11
12
  • 从此案例可以看出 $* 和 $@ 的区别

# 🖊 基本语法 2

for (( 初始值;循环控制条件;变量变化 ))
do
      程序
done
1
2
3
4

For Example:

#!/bin/bash
# 案例:从1加到100的值输出显示
# 定义一个变量 SUM
SUM=0
for (( i=1;i<=100;i++ ))
do
      SUM=$[$SUM+i]
done
echo "总和SUM=$SUM"
1
2
3
4
5
6
7
8
9

# 6.4 while 循环

while [ 条件判断式 ]
do
      程序
done
1
2
3
4
  • 注意:[ 条件判断式 ] 中括号和条件判断式之间必须有空格,while 与 [ 之间也有空格

For Example:

#!/bin/bash
# 案例:从命令行输入一个数 n,统计从 1+...+ n 的值
SUM=0
i=0
while [ $i -le $1 ]
do
      SUM=$[$i+$SUM]
      i=$[$i+1]
done
echo "总和SUM=$SUM"
1
2
3
4
5
6
7
8
9
10

# 7. read 读取控制台输入

read [选项] [参数]

  • -p:指定读取值时的提示符;
  • -t:指定读取值时等待的时间(秒),如果没有在指定的时间内输入,就不再等待了。
  • 参数:指定读取值的变量名

For Example:

#!/bin/bash
# 案例1:读取控制台输入一个 NUM1 值
read -p "请输入一个数NUM1=" NUM1
echo "您输入数NUM1=$NUM1"
# 案例2:读取控制台输入一个 NUM2 值,在 10 秒内输入
read -t 10 -p "请输入一个数NUM2=" NUM2
echo "您输入数NUM2=$NUM2"
1
2
3
4
5
6
7

# 8. 函数

shell 编程有系统函数,也可以自定义函数。

# 8.1 系统函数

这里只抛砖引玉地介绍两个系统函数

# 8.1.1​ basename 函数

功能:返回完整路径最后 / 的部分,常用于获取文件名

  • basename [pathname] [suffix]
  • basename [string] [suffix]:会删掉所有的前缀包括最后一个(‘/’)字符,然后将字符串显示出来。

选项:suffix 为后缀,如果 suffix 被指定了,basename 会将 pathname 或 string 中的 suffix 去掉。

案例:请返回 /home/aaa/test.txt 的 test.txt 部分

  • basename /home/aaa/test.txt
  • 如果我们指定后缀 suffix,即 basename /home/aaa/test.txt .txt,那么显示的只有 test

# 8.1.2​ dirname 函数

功能:返回完整路径最后 / 的前面的部分,常用于返回路径部分。

  • dirname 文件绝对路径:从给定的包含绝对路径的文件名中去除文件名(非目录的部分),然后返回剩下的路径(目录的部分)。

案例:请返回 /home/aaa/test.txt 的 /home/aaa 部分

  • dirname /home/aaa/test.txt

# 8.2 自定义函数

[ function ] funname[()]
{
      Action;
      [return int;]
}
1
2
3
4
5

调用直接写函数名:funname [值]

For Example:

#!/bin/bash
# 案例1:计算输入两个参数的和(动态获取),getSum
# 定义函数 getSum
function getSum()
{
      SUM=$[$NUM1+$NUM2] 
      echo "和是=$SUM"
}
# 输入两个值
read -p "请输入一个数NUM1=" NUM1
read -p "请输入一个数NUM2=" NUM2
# 调用自定义函数
getSum $NUM1 $NUM2
1
2
3
4
5
6
7
8
9
10
11
12
13

可以看出,shell 编程与我们常用的编程语言还是不太一样的,既然学了就看开一点。

# 9. 综合案例

需求:

  • 每天凌晨2:10,备份数据库 hspedu 到 /data/backup/db
  • 备份开始和备份结束能够给出相应的提示信息
  • 备份后的文件要求以备份时间为文件名,并打包成 .tar.gz 的形式,比如:2018-03-12_230201.tar.gz
  • 在备份的同时,检查是否有 10 天前备份的数据库文件,如果有就将其删除。
  • 编写一个 Shell 脚本。

shell 脚本:

#!/bin/bash
#完成数据库的定时备份
#备份的路径
BACKUP=/data/backup/db
#当前的时间作为文件名
DATETIME=$(date +%Y-%m-%d_%H%M%S)
#可以输出变量调试
#echo ${DATETIME}
echo "======================开始备份=========================="
echo "========备份的路径为 $BACKUP/$DATETIME.tar.gz =========="
#数据库地址
HOST=localhost
#数据库用户名
DB_USER=root
#数据库密码
DB_PW=root
#备份数据库名
DATABASE=hspedu
#创建备份的文件夹
#如果该备份的文件夹有则使用,没有就重新创建一个。
#-p:表示递归创建目录,或者说创建多级目录。
[ ! -d "${BACKUP}/${DATETIME}"] && mkdir -p "${BACKUP}/${DATETIME}"
#执行 MySQL 备份数据库的指令
mysqldump -u${DB_USER} -p${DB_PW} --host=${HOST} ${DATABASE} | gzip > ${BACKUP}/${DATETIME}/$DATETIME.sql.gz
#将文件处理成 tar.gz
cd ${BACKUP}
tar -zcvf $DATETIME.tar.gz ${DATETIME}
# 删除对应的备份目录
rm -rf ${BACKUP}/${DATETIME}
#删除10天前的备份文件
find ${BACKUP} -atime +10 -name "*.tar.gz" -exec rm -rf {} \;
echo "=====================备份文件成功========================="
1
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

运行 crontab -e 指令,增加下面命令(结尾光标要停留在 h 上),用于定时调用上面的 Shell 脚本:

10 2 * * * /usr/sbin/mysql_db_backup.sh
1
编辑 (opens new window)
上次更新: 2023/06/07, 13:42:57
JavaEE 与 Python 定制篇
学习 Shell

← JavaEE 与 Python 定制篇 学习 Shell→

最近更新
01
Deep Reinforcement Learning
10-03
02
误删数据后怎么办
04-06
03
MySQL 一主多从
03-22
更多文章>
Theme by Vdoing | Copyright © 2021-2024 yubincloud | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×