TimothyQiu's Blog

keep it simple stupid

自动备份

分类:技术

我已经忘了上次备份服务器是什么时候的事了,只记得前不久因为「以为备份过」做错事然后手忙脚乱了。更考虑到自己邮箱那么多空间浪费着也是浪费着,于是就决定抽空写个脚本定期备份一下。

说是「自动」备份,其实一句话解释就是通过 cron 定期执行脚本,对需要备份的目录使用 tar 打包,然后 sendmail 发到邮箱。

这里的操作系统是 CentOS,其它系统应该也是大同小异。以下便是本次行动的流水帐 :)

Sendmail 相关

因为系统里原生没有安装 sendmail,于是

yum install sendmail   # 安装
service sendmail start # 启动服务
chkconfig sendmail on  # 开启自动启动

因为只想使用发信功能,所以也不用花时间改配置(收信功能的默认配置是只接受本地邮件)。确定防火墙允许 25 端口后,就可以使用下面的命令直接发送邮件了。

# 使用交互模式(使用只包含一个句点的行结束正文并发送)
mail <收件人>
# 命令行直接发送
echo <正文> | mail -s <主题> <发件人>

使用这种方法发出的邮件显示发件人地址为「<用户名>@<主机名称>」。因为 sendmail 只支持纯文本,所以需要借助 uuencode 来编码附件,于是:

# 只包含附件
uuencode <文件路径> <文件显示名称> | mail -s <主题> <收件人>
# 正文和附件
(echo <正文>; uuencode <文件路径> <文件显示名称>) | mail -s <主题> <收件人>

备份脚本

编写 Shell 脚本实现对 Web 目录的备份和发送工作,因为博客使用的是 SQLite 数据库,直接打包即可,不需要先用 mysqldump 导出。(新手上路,表示快被引号什么的弄疯了,于是大多数情况下都为变量加上了引号。另外邮件正文纯属无聊,完全可以不加 = =)

#!/bin/sh

# 备份文件存放目录
BACKUP_DIR="/path/to/backup/directory"

# 备份文件邮件接收地址
BACKUP_MAIL_RECEIVER="example@example.com"

# =====================================

# 保证目录存在
[ ! -d "$BACKUP_DIR" ] &&  mkdir -p "$BACKUP_DIR"

# 需要备份的路径
WEB_DIR="/path/to/web/root"

BACKUP_DATE="`date +%Y-%m-%d`"

BACKUP_FILE_NAME="$BACKUP_DATE.tar.gz"
BACKUP_FILE_FULLNAME="$BACKUP_DIR/$BACKUP_FILE_NAME"

cd $WEB_DIR
    tar czpf "$BACKUP_FILE_FULLNAME" *

BACKUP_MAIL_SUBJECT="VPS Backup: $BACKUP_DATE"
BACKUP_MAIL_MESSAGE=$(
    echo "Sir,";
    echo "";
    echo "Backup file $BACKUP_FILE_FULLNAME created.";
)

(echo "$BACKUP_MAIL_MESSAGE"; uuencode "$BACKUP_FILE_FULLNAME" "$BACKUP_FILE_NAME") \
    | mail -s "$BACKUP_MAIL_SUBJECT" "$BACKUP_MAIL_RECEIVER"

保存后给脚本加上执行权限,就可以直接运行试一下,应该会有邮件发到邮箱了。

chmod u+x <脚本名>

定时执行

定时执行肯定就是 cron 了,这个不用多说,直接 crontab -e 然后添加一行:

0 0 * * 2 /path/to/script

就大功告成鸟~从此,每周二午夜就会有一封谜之信件安静地躺到你的邮箱里……

SSH Escape Sequences

分类:技术

前阵子因为要折腾 VPS 的缘故把博客暂时放在了 @likounin 童鞋的服务器上,不想最近他的服务器出了点问题,于是只能赶紧把 VPS 重新折腾回原样,然后把博客搬回来。

慌忙折腾之中遇到个问题:在 SSH 里卡住了,没法 Ctrl-C(因为这种按键会直接发给远端),该怎么正常退出?

想来也应该有个转义序列可以直接对 SSH 做操作……于是,那就是 ~ 键。

SSH 到服务器后,确保之前按过一次回车,然后按

~?

于是就会列出一堆可用的转义序列,比如:

Supported escape sequences:
~. - terminate connection
~B - send a BREAK to the remote system
~C - open a command line
~R - Request rekey (SSH protocol 2 only)
~^Z - suspend ssh
~# - list forwarded connections
~& - background ssh (when waiting for connections to terminate)
~? - this message
~~ - send the escape character by typing it twice
(Note that escapes are only recognized immediately after newline.)

总之,要退出已经卡住的 SSH 程序,可以在按一次回车(见上面帮助信息的最后一行)之后,按下

~.

话说回来,转义字符也只是默认是这个 ~ 字符而已,如果不喜欢的话,ssh_config 文件中有一个选项叫 EscapeChar 可以修改转义字符。

以上。

睡眠排序算法

分类:技术

4chan 上有个家伙发帖说他发明了一种很牛叉的排序算法:睡眠排序(Sleep Sort):

#!/bin/bash
function f() {
    sleep "$1"
    echo "$1"
}
while [ -n "$1" ]
do
    f "$1" &
    shift
done
wait

主要就是对输入的每一个数都新开一个进程,进程里用这个数进行倒数,倒数到0就输出这个数。于是较小数就先输出、较大数后输出。睡排成功~

当然啦群众的眼睛是雪亮的,纷纷指出这个会存在竞态条件,而且如果的数比较大会很悲催(比如要排的数字里有86400的话,那么至少要等86400秒,也就是一整天 =。=),时间复杂度是 O(最大的那个数)……

在这个欢乐的帖子里还看到了各种其它语言对睡眠排序的实现,包括一个 Lua 的(#165):

#!/usr/bin/env lua
function sleepsort(arr)
    local res, thread = {}, {}
    local nthreads = #arr

    for i = 1, #arr do
        thread[i] = coroutine.create(function()
            for n=arr[i], 0, -1 do coroutine.yield() end
            nthreads = nthreads - 1
            thread[i] = nil
            res[#res+1] = arr[i]
        end)
    end
    while nthreads > 0 do
        for i = 1, #arr do
            if thread[i] then coroutine.resume(thread[i]) end
        end
    end
    return res
end

math.randomseed(os.time())
local arr = {}
for i = 1,10 do arr[i] = math.random(1,99) end
print(unpack(sleepsort(arr)))

嗯哼~ Lua 的 coroutine 还是很强大滴(<ゝω·)

p.s. 终于还是见识到了最无厘头撞大运的 Bogo 排序算法

while not inOrder(deck) do
    shuffle(deck);