周游Linux操作系统——Shell编程

作者:Rhythm_2019

Emial:rhythm_2019@163.com

学习时间:2021.02.11

终于来到了最重要的部分了,Shell类似于Window的批处理脚本,它可以很方便的执行一系列的指令

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

脚本格式要求

  1. 必须以#!/bin/bash开头
  2. 必须有执行权限

Hello world

只需要创建一个sh文件,写入下面内容即可

1
2
#!/bin/bash
echo "hello world"

使用chmod给予执行权限,直接./hello.sh即可

如果你不想给予权限,你可以使用sh指令执行你的脚本

1
sh my.sh

变量

Linux中的变量分为系统变量和用户自定义变量

  1. 系统变量:$HOME、$PWD、$USER等等,我们可以使用set指令查看
  2. 自定义变量:命名要求和C一样,直接变量名=值即可
    • 撤销变量:unset 变量名
    • 声明静态变量:readonly 不可以用unset释放

我们要注意两点:

  1. 我们习惯使用全大写命名
  2. 等号左右没有空格

接收返回值

  1. 使用$(command)j,注意是小括号
  2. 使用`command`

设置环境变量

其实刚刚已经弄过了,只需要在etc/profile里面机上

1
2
A=B
export A

即可,然后使用eho $A查询他得值

位置参数变量

当我们执行shell脚本的时候,我们其实可以带参数的,像Java一样

1
./my.sh 100 200

我们可以在脚本里面获取到这些值

1
2
echo "第一个参数时$1"
echo "第二个参数时$2"

这个变量叫做位置参数,可以从1数到9,第十个要这样写${10}。还有下面几个比较常用的符号

  • $* 表示所有参数(看作整体)
  • $@ 表示所有参数(区分)
  • $# 表示参数的数量

其中,$*$@的区别体现在for循环的时候,Linux的forPython的差不多,都是用in形式的

预定义变量

其实就是一些约定熟成的变量

  1. $$ 当前PID
  2. $! 后台最后一个运行的进程ID
  3. $? 最后一次执行的指令的返回状态,如果是0说明运行正常,非0表示命令运行可能出现了什么问题

运算符

加减成熟,有两种形式

  1. $(())或者$[]:里面直接写算式,比较喜欢这种
  2. expr 表达式 其中表达式符号前后要有空格

比如计算 (3 + 8) * 8

1
2
3
4
5
6
echo $[(3+5)*8]
echo $(((3+5)*8))

TMP=`expr 3 + 5`
RES=`expr $TMP \* 8`
echo $RES

使用expr要注意以下几点

  1. 字符转义
  2. 使用变量保存结果时可以用``或者$()

大家可以多尝试一下

条件判断

[ condition ](注意 condition 前后要有空格)

我们可以这样写

1
[ condition ] && 语句1 || 语句2

来控制逻辑,也可以使用$?验证

1
2
[ 111 ]
echo $?

如果是true则显示0,否则显示1

常用的判断:

  1. 整数的比较:
    1. = 字符串比较
    2. -lt 小于
    3. -le 小于等于
    4. -eq 等于
    5. -gt 大于
    6. -ge 大于等于
    7. -ne 不等于
  2. 根据文件权限判断
    1. r 是否可读
    2. -w 是否有写权限
    3. -x 是否有执行权限
  3. 根据文件类型判断
    1. -f 文件存在且是个常规文件
    2. -e 文件存在
    3. -d 文件存在且是个文件夹

大家自己试试什么效果哈

1
2
[ -d /usr/local/mysql ]
echo $? #0

流程控制

主要涉及ifcasewhilefor语句

if语句

两种形式

1
2
3
if [ condition ]; then
语句
fi

或者

1
2
3
4
5
6
if [ condition ] 
then
语句
elif [ condition ]
then
fi

case语句

1
2
3
4
5
6
7
8
9
10
11
case $变量名 in
"value1")
语句1
;;
"value2")
语句2
;;
*)
默认执行
;;
esca

for语句

1
2
3
4
for 变量名 in 值1 值2...
do
语句
done

或者

1
2
3
4
for ((i=0;i<=10;i++))
do
语句
done

while语句

1
2
3
4
while [ condition ] 
do
语句
done

read语句

用户输入,有两个参数

  • p 指定提示内容
  • t 指定等待时间

函数

函数有分系统提供的系统函数和自己写的自定义函数

常用的系统函数有下面两个:

  1. basename 路径 [后缀]

    返回文件名,如果你指定了后缀则只返回前缀

  2. diname l路径

    返回路径

自定义函数的写法如下

1
2
3
4
5
6
7
function func() {
echo $1
echo $2
echo $3
}

func(11. 12, 13)

形参可以不写,直接传过去就行,参数可以用$n拿到参数

综合案例

需求

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

我觉得老师的思路非常清晰,看完以后自己也来谢谢,备份可以使用mysqldump来实现

1
2
3
4
5
6
7
# 备份
mysqldump -uroot -p --all-databases #备份所有
mysqldump -uroot -p 数据库 # 备份某个数据库

# 还原
mysqladmin -uroot -p create db_name
mysql -uroot -p db_name < /backup/mysqldump/db_name.db

卧槽写了一个下午

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/bin/bash

# Authpr: Rhythm_2019
# Date: 2021.02.11 (除夕)


# 常量定义
readonly DATABASE_NAME="community"
readonly USER_NAME=root
readonly PASSWORD=123456

echo $DATABASE_NAME$USER_NAME
readonly TARGET_DIR="/data/mysql_backup/"
readonly MYSQL_DUMP="/usr/local/mysql/bin/mysqldump"

BACKUP_FILE_NAME=${DATABASE_NAME}_$(date +%Y%m%d_%H%M%S).bak

echo "准备自动备份MySQL指定数据率 $DATABASE_NAME,当前时间:"$(date)

# 创建文件夹
if [ ! -d TARGET_DIR ]
then
mkdir -p $TARGET_DIR
fi


if [ ! -e $MYSQL_DUMP ] || [ ! -x $MYSQL_DUMP ]
then
echo "找不到MySQL备份程序或没有权限执行"
exit
fi

# 开始备份
echo "=================== 开始备份 ===================="

# 备份
$MYSQL_DUMP -u$USER_NAME -p$PASSWORD $DATABASE_NAME | gzip > /tmp/$BACKUP_FILE_NAME.gz

# 清楚之前备份的文件
find $TARGET_DIR -name ".tar.gz" -mtime +10 -exec rm -rf {} \;

# 压缩
cd /tmp/
tar -czvf $TARGET_DIR/$BACKUP_FILE_NAME.tar.gz $BACKUP_FILE_NAME.gz

# 删除临时文件
#find -name ${DATABASE_NAME}* -exec rm -rf {} \;

echo "================== 备份完成 ======================"

我以为它很简单的,结果折腾了很久,可能有一些细节之前学习的时候也没留意,这里说一下

  1. 关于这个$符号:

    可以$变量名${变量名}$(指令),如果你只是单独想输出一下变量值可以用$变量名,但是如果你希望做一些字符换拼接你可能就需要后者,比如你希望拼接路径

    1
    BACKUP_FILE_NAME=${DATABASE_NAME}_$(date +%Y%m%d_%H%M%S).bak
  2. 字符串常量带不带双引号都行

  3. 关于解压的两个指令

    文件 > 目标文件```的形式压缩
    1
    2
    3
    4
    5

    ```tar```指令不建议使用绝对路径,如

    ```shell
    tar -zcvf /opt/a.tar.gz /tmp/a.txt

    如果你是开发tar指令的程序员,你一定会感到苦恼,构建压缩文件的时候是先构建tmp目录再构建a.txt呢,还是直接构建a.txt的压缩包呢

    你可以使用-P指定构建多一级目录,或者先cd进去再压缩

  4. find指令可以配合-exec 指令使用,记得最后需要加上\;表示语句结束

最后把他加到crond里面就好

1
crontab -e

添加一行

1
0 2 * * * /home/testsh/backup.sh

就好


关于Shell的所有内容就是这些啦,其实Linux还有好多指令我还没接触到,以后用到了会陆陆续续往博客中添加。好啦,刚好十二点,祝大家牛年大吉,万事顺意!