menu Chancel's blog
rss_feed
Chancel's blog
我就是这样的人

学习使用Bash制作一键备份文件夹和MySQL的通用脚本

作者:Chancel, 更新:2019 Dec 05, 字数:6594, 已阅:722

这篇文章更新于 1270 天前,文中部分信息可能失效,请自行甄别无效内容。

服务器上的备份脚本是好几年前写的,前段时间发现不工作了,准备检修下,简单看了下,这语法写的实在是太糟糕了(练习时长2年半的bug练习生涯过后果然看不起以前写的bug了),正好最近打算学习一下Bash Shell脚本的语法,就趁着这个机会学习下

介绍

bash?zsh?shell?都是什么?

Unix shell,引用wiki的说明

A Unix shell is a command-line interpreter or shell that provides a command line user interface for Unix-like operating systems. The shell is both an interactive command language and a scripting language, and is used by the operating system to control the execution of the system using shell scripts.

Users typically interact with a Unix shell using a terminal emulator; however, direct operation via serial hardware connections or Secure Shell are common for server systems. All Unix shells provide filename wildcarding, piping, here documents, command substitution, variables and control structures for condition-testing and iteration.

Unix shell是一个命令行解释器或shell,为类Unix操作系统提供命令行用户界面。

shell既是交互式命令语言又是脚本语言,操作系统使用它来控制使用shell脚本执行系统。 用户通常使用终端仿真器与Unix shell进行交互; 但是,通过串行硬件连接或Secure Shell直接操作对于服务器系统是常见的。 所有Unix shell都提供文件名通配符,管道,这里的文档,命令替换,变量和控制结构,用于条件测试和迭代。

简单讲就是Shell是一种命令语言 Shell目前主要有两大主流流派

  • SH
    • burne shell(sh)
    • burne again shell(bash)
  • csh
    • c shell(csh)
    • tc shell(tcsh)
    • korn shell(ksh)

大部分Linux发行版都是预设bash环境,bash是shell的一种,shell是一种统称符合其语法规则语言的简称

快速掌握Bash语法

创建一个脚本文件

快速的看了下Bash的语法,看完很快,但教程网站好像都是写给新手看的,很多地方有经验的程序员根本不需要唠嗑那么多,这里我把语法点跟例子整理出来以供参考 脚本一般有开头标记来告诉环境需要什么脚本解释器,linux下script大部分情况下都是由bash解释器来执行的

#!/bin/bash
echo "Hello World !"

如何执行它?保存为一个hello.sh文件,赋予权限,并执行

chmod +x hello.sh
# 调用1. 使用指定解释器执行
/bin/bash hello.sh
# 调用2. 因为文件开头已经写了执行器类型,直接调用(./指当前目录,如有不理解请自行搜索)
./hello.sh

使用变量

看看变量的创建调用与传递

#!/bin/bash
pick_hero="superman"
# 使用1. 不带花括号
echo $pick_hero is me
# 使用2. 带花括号(推荐写法)
echo ${pick_hero} is me

变量命名规则

  • 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头
  • 中间不能有空格,可以使用下划线(_)
  • 不能使用标点符号
  • 不能使用bash里的关键字(可用man命令查看保留关键字)

变量其他写法一览

#!/bin/bash
myName="chancel"
# 设置只读,如无设置只读则变量可以无限次重复定义
readonly myName
mybook="None"
# 删除变量
unset myBook

# 拼接字符串
str01="hello"
str02="world"
echo ${str01}" "${str02}

# 定义数组
myArray = ("x" "y" "z")
echo $(myArray[0]) #取出x元素
echo ${myArray[×]} #取出所有元素
echo ${myArray[@]} #取出所有元素
# 遍历1
for var in ${myArray[@]}
do
    echo $var
done
# 遍历2
for i in "${!myArray[@]}"
do
    printf "%s\t%s\n" "$i" "${myArray[$i]}"
done
# 遍历3
i=0
while [ $i -lt ${#myArray[@]}]
do
    echo ${myArray[$i]}
    let i++
done

参数传递

我们经常使用下面这种方法调用脚本

./xswl.sh -who chancel -why cxk

这种做法一般需要检查用户输入,下面是一个完整的方法

#!/bin/bash -e
show_help() {
    echo "$0 [-h|-?|--help] [--who me] [--why hhh]"
    echo "-h|-?|--help    显示帮助"
    echo "--who           输入你是谁"
    echo "--why           为什么笑"
}

while [[ $# -gt 0 ]]; do
    case $1 in
    -h | -\? | --help)
        show_help
        exit 0
        ;;
    --who)
        WHO="${2}"
        shift
        ;;
    --why)
        WHY="${2}"
        shift
        ;;
    *)
        echo -e "Error: $0 invalid option '$1'\nTry '$0 --help' for more information.\n" >&2
        exit -1
        ;;
    esac
    shift
done

调用效果

$ ./xswl.sh -h
./xswl.sh [-h|-?|--help] [--who me] [--why hhh]
-h|-?|--help    显示帮助
--who           输入你是谁
--why           为什么笑

if else

bash是如何控制流程的,参考下面的例子 if-else规则

if condition
then
    command1 
    command2
    ...
    commandN 
fi

if-elseif-else

if condition1
then
    command1
elif condition2 
then 
    command2
else
    commandN
fi

一个简单的例子

#!/bin/bash -e
x=100
y=200
if [ $x == $y]
then
    echo "x = y"
elif [ $x -gt $y ]
then
    echo "x > y"
else
    echo "x < y"

运算符

上面涉及到运算符,bash的运算符相对简单

参数 说明
-eq =
-ne !=
-gt >
-ge >=
-lt =<
-le <=
-z(字符串) 字符串长度为零
-n(字符串) 字符串长度不为零

下面是例子

#!/bin/bash

# 加减乘除运算
x=100
y=200
$z=$[x+y] #这语法有点奇怪,且实测等号两侧不可空格(写惯了c#可能很不习惯这种写法)
echo $z

# 字符串判断
x="hello"
y="hallo"
if test $x = $y
then
    echo "字符串内容相同"
else
    echo "字符串内容不同"
fi

到这写一个最基础的服务器备份脚本知识点基本都齐了,如果对其他语法点还感兴趣,可以参考下面进行学习

bash中文手册Handbook - 时光小栈

日常常见的语法还有下面这些

  • printf
  • function
  • 输入输出重定向
  • 脚本引入
  • 关于文件的运算符

备份脚本

我的需求主要有以下2点

  • 备份MySQL中除去自带数据库外的所有数据库
  • 备份/opt目录下的所有程序

程序思路

  • 创建/tmp/_backup文件夹
  • 导出数据库文件到/tmp/_backup/mysql_backup文件夹
  • 压缩指定文件夹与数据库备份文件夹到/tmp/_backup/日期.tar.gz
  • 删掉目标文件夹的旧备份并移动备份文件到目标文件夹(比如Sycnthing与我的Nas同步的文件夹下)
  • 删掉/tmp/_backup文件夹

于是经过上面的学习以及备份思路,你可以得到以下这份脚本,里面的所有知识点基本都在上面提到了,只有date函数没有提到(这个比较复杂,请自行搜索相关资料参阅)

#!/bin/bash
# author:chancel
# url:www.chancel.cn

show_help() {
    echo "$0 [-h|-?|--help] [--temp /tmp/_backup] [--target /opt/backup/] [--dbuser root] [--dbpasswd passwd] [--extra /opt]"
    echo "-h|-?|--help    显示帮助"
    echo "--temp          设置备份文件时的缓存目录"
    echo "--target        设置备份文件的目标(存放)路径"
    echo "--dbuser        设置mysql数据库用户名称"
    echo "--dbpasswd      设置mysql数据库用户密码"
    echo "--extra         额外需要备份的目录,以空格分开不同目录"
}

while [[ $# -gt 0 ]]; do
    case $1 in
    -h | -\? | --help)
        show_help
        exit 0
        ;;
    --temp)
        temp="${2}"
        shift
        ;;
    --target)
        target="${2}"
        shift
        ;;
    --dbuser)
        dbuser="${2}"
        shift
        ;;
    --dbpasswd)
        dbpasswd="${2}"
        shift
        ;;
    --extra)
        extra="${2}"
        shift
        ;;
    --)
        shift
        break
        ;;
    *)
        echo -e "错误: $0 无效操作 '$1'\n可输入命令 '$0 --help' 获取更多帮助.\n" >&2
        exit -1
        ;;
    esac
    shift
done

echo "创建必要的文件夹"
mysql_backup_dir=$temp'/mysql_backup'
mkdir -p $mysql_backup_dir
new_backup_file_path=$temp"/`date +%Y%m%d`.tar.gz"
old_backup_file_path=$target"/`date -d -1day +%Y%m%d`.tar.gz"

echo "备份mysql数据库"
databases=`mysql -u$dbuser -p$dbpasswd -e "SHOW DATABASES;" | tr -d "| " | grep -v Database`
for db in $databases; do
    if [[ "$db" != "sentry" ]] && [[ "$db" != "information_schema" ]] && [[ "$db" != "performance_schema" ]] && [[ "$db" != "mysql" ]] && [[ "$db" != _* ]] ; then
        echo "正在导出数据库$db"
        mysqldump -u$dbuser -p$dbpasswd --databases $db > $mysql_backup_dir'/'`date +%Y%m%d`-$db.sql
    fi
done


echo "正在删除上一次产生的备份包"
rm -f $old_backup_file_path

echo "创建备份文件"$new_backup_file_path
tar -zcvf $new_backup_file_path $mysql_backup_dir $extra
mv $new_backup_file_path $target

echo "删除临时文件夹"$temp
rm -rf $temp
echo "备份结束"

参考这份文档,根据你的需要自定修改你就可以快速得到一份备份脚本


[[replyMessage== null?"发表评论":"发表评论 @ " + replyMessage.m_author]]

account_circle
email
web_asset
textsms

评论列表([[messageResponse.total]])

还没有可以显示的留言...
[[messageItem.m_author]] [[messageItem.m_author]]
[[messageItem.create_time]]
[[getEnviron(messageItem.m_environ)]]
[[subMessage.m_author]] [[subMessage.m_author]] @ [[subMessage.parent_message.m_author]] [[subMessage.parent_message.m_author]]
[[subMessage.create_time]]
[[getEnviron(messageItem.m_environ)]]
目录