前几天刚把博客迁移了过来,顺便更了两篇文章。想着每次写完文章(或者对原来的文章作改动)就要手动部署下有点麻烦,尝试寻找自动更新博客方案。

fswatch

实现自动更新博客关键在于检测文件变动,根据这个思路我找到了fswatch

fswatch是一个跨平台的文件更改监视器,在指定的文件或目录的内容被改变或修改时会做出通知。man fswatch得到的描述如下:

fswatch -- Ask for notification when the contents of the specified files or directory hierarchies are modified.

用brew安装好后,简单使用一下,fswatch的用法是:

fswatch (option)* path+

下面demo包含了fswatch监控文件创建,修改与删除操作:

fswatch监控文件创建,修改和删除

display dialog

若终端脚本在后台运行时出现异常(自动部署失败),不能只是写入日志还需要主动提醒(例如弹窗),在macOS下我找到了AppleScript。

Applescript是适用于macOS的一种脚本语言,可以用于直接控制macOS上的应用程序,来实现macOS的自动化。这里我用AppleScript中的display dialog来实现弹窗功能:

#!/usr/bin/env osascript

tell app "System Events"
    display dialog "required dialog text" buttons {"optional button text 1", "optional button text 2"} with title "optional dialog title text"
    get the button returned of the result
end tell

运行上面这个脚本将弹出一个dialog,窗口内容为”required dialog text”,窗口内有两个按钮”optional button text 1”和”optional button text 2”,窗口名为”optional dialog title text”,点击按钮后脚本返回所选按钮的文本。

AppleScript需要用osascript命令来运行:

# 方式一:
osascript applescript.scpt
# 方式二: 
osascript -e 'script command'
# 方式三: 使用heredoc: http://www.tldp.org/LDP/abs/html/here-docs.html, 这种方式可以很好地解决bash到AppleScript的参数传递问题
osascript <<-'END'
script command
END

自动部署博客脚本

利用fswatchdisplay dialog可以得到如下auto-blog-submit.sh脚本:

BLOG_PATH=/Users/barriery/Blog/ # your blog root path
LOG_PATH=${BLOG_PATH}/auto-blog-submit.log
ARTICLE_PATH=${BLOG_PATH}/source/_posts

function showDialog(){
osascript -e 'tell app "System Events"
display dialog "[ERROR] Hexo deploy failed." buttons {"View Log", "Cancel"} default button "View Log" with title "auto-blog-submit.sh"
get the button returned of the result
end tell'
}

cd ${BLOG_PATH} # Otherwise the script will run in the home path
if [ ! -f ${LOG_PATH} ]; then
    touch ${LOG_PATH}
fi

nohup fswatch -o ${ARTICLE_PATH} -l 900 | while read
do
    date "+%Y-%m-%d %H:%M:%S" >> ${LOG_PATH}
    hexo g -d >/dev/null  2>> ${LOG_PATH}

    if [ $? -ne 0 ]; then
        echo "\033[31m[ERROR]\033[0m Hexo deploy failed." >> ${LOG_PATH}
        say "自动部署博客脚本出现异常"

        botton_returned=$(showDialog)
        echo "\033[33m[INFO]\033[0m You choose \033[33m${botton_returned}\033[0m" >> ${LOG_PATH}

        # ${botton_returned} 加引号防止变量中空格造成影响
        if [ "${botton_returned}" = "Cancel" ]; then
            : # Do nothing beyond expanding arguments and performing redirections. The return status is zero.
        elif [ "${botton_returned}" = "View Log" ]; then
            open -a Sublime\ Text ${LOG_PATH} # Using Sublime to open log file.
        fi
    fi
done &

其中,fswatch-o参数是仅输出监控到的事件数;-l参数指定了潜伏时间,这里结束一次监控后潜伏900秒(默认为1秒)做下一次监控。

该脚本放监控${BLOG_PATH}/source/_posts中的文件(也就是自己写的文章放置的位置),当文件变动时会将log写入${BLOG_PATH}/auto-blog-submit.log中,并重新部署博客。当脚本出现异常的时候,语音播报”自动部署博客脚本出现异常”,并弹出窗口提示脚本出现异常。

开机自动运行脚本

需要开机运行脚本才能使得博客能够一直更新。

Mac上设置开机自动运行如下:

系统偏好设置 -> 用户与群组 -> 当前用户 -> 登录项 -> 添加脚本

同时设置脚本默认打开方式(iTerm或Terminal)并需要给予脚本root权限:

chmod 777 auto-blog-submit.sh

About

刚开始测试脚本的时候,发现开机自动运行会保留终端窗口,所以需要挂在后台运行,用到nohup

nohup可以让程序始终在后台执行,即使关闭当前的终端或退出账户也能继续执行(而用&关闭终端则会停止运行)。用法:

nohup command &

关闭终端后,在另一个终端用jobs已经无法看到后台跑的程序了,此时利用ps进程查看命令:

ps -Ao pid,user,start,command | grep "auto-blog-submit.sh" # Linux: ps -aux | grep "auto-blog-submit.sh"

找到进程号后,开源用kill杀进程:

kill -9 进程号