rsync –exclude-from 的文件的格式

类似这样的 rsync 命令同步时,在指定的文件(list.exclude)中,列出所有不需要同步的文件路径列表。默认是一行一个文件。

rsync -avz --exclude-from list.exclude ..

今天同步专辑的时候,发现有三个目录虽然在 exclude 列表中,但依然被同步了:

Bo Burnham – Bo Burnham [Explicit]
Justin Timberlake – Futuresex_Lovesounds [Deluxe Edition]
T.I_ – Paper Trail [EXPLICIT]

共同的特点是都包含方括号。于是尝试用单引号封起来,但实践证明 rsync 不认,它会把单引号视作文件名的一部分。在对方括号转义后,问题解决。

Bo Burnham – Bo Burnham \[Explicit\]
Justin Timberlake – Futuresex_Lovesounds \[Deluxe Edition\]
T.I_ – Paper Trail \[EXPLICIT\]

后来仔细一想,本质上,[]*? 是一样的,都用来告诉 bash 扩展文件列表。所以上面带方括号的专辑名称一扩展,rsync 就找不到了,亦即不在 exclude 列表。

bash 中使用 vi 风格编辑命令

有时候输入的命令比较长比较复杂,使用方向键或者 delete 键修订新命令的时候很累赘。可以输入:

set -o vi

告诉 bash 使用 vi 风格的方式编辑。此后,按 Escape 键即可进入类似 vi 的命令模式,然后就当作在 vi 里面编辑一行文字一样操作,简洁无比。若要每次使用 bash 都自动打开此风格,可以把上面这条命令放到 ~/.bash_profile 里面,并确保 ~/.bashrc 中加载此 profile 文件:

source ~/.bash_profile

应该有很多人知道这种用法。我向来后知后觉,上上个礼拜才知道,今天比较无聊,记下来,应该还是有很多人可能不知道。介绍下总比不介绍好,呵呵。

Google Ads

使用 curl 提交 twitter 消息

一直对 twitter 不怎么感冒,貌似没有必要让朋友知道我每时每刻的行踪,也没有朋友需要了解我的 timeline。不过随手一句话用来发发牢骚。做个小笔记什么的还是蛮不错的,特别是当提交信息的方式可以离线用手机发。 与此同时,twitter 的 API 提供了无限的创造力和可能性。除了在 Gmail 里向 twitter bot 发消息外,最便捷的莫过于在随时开着的终端 shell 里面写上一句然后敲回车。 找来这段脚本,用 curl 一下搞定。

#!/bin/bash
read -p "twitter> " -e input
curl --basic --user username:password --data status="$input" http://twitter.com/statuses/update.xml > /dev/null
echo "OK"

修改其中的用户名密码,保存为 twitter ,再 +x 后扔到 /usr/local/bin 里面,然后就可以直接通过执行这个命令来发消息了。十分 handy 的东西。

jQuery.js 和 Prototype.js

实在忍不住要写点什么了。我被这两个 JavaScript 搞死了。它们都让我又爱又恨。

Prototype.js 出来得早,随着 Rails 的开发而逐渐扩大影响力,成为 Rails 框架下御用 JS 框架。所以秉承了 Ruby 语言的一系列概念,非常清晰地用组织良好的类,对象扩展的方式,增加了各种常用的 handy 函数或者方法。所以叫做原型么。学习曲线低,顺手拈来。

不过这种循规蹈矩的做法很快让书写它们的人感到厌倦。很多操作都要加上类名,繁复、生硬、罗嗦。于是 jQuery.js 得以进化为,用最自然的,类似伪代码的形式,完成相同的功能,并具有更好的可读性。比如:

$(".rborder").corner("5px").parent().css('padding', '1px').corner("round 5px");

一气呵成,痛快极了。想象不出还有什么写法可以比这个更简洁达意。而 jQuery 的哲学就是:写最少的代码,做最多的事情。

而这种写法,像极了 shell 下面使用管道符串联起来的一系列命令,简单、优雅、强大、灵活。喜欢用 Perl 写代码的一个原因,也是因为其中的 map 和 grep 组合所构造出来的类似效果,清晰、流畅、自然。所以 jQuery 是很讨开发者的欢心的,用起来舒服。

用起来舒服,这件事本身没什么伟大的,可一舒服,写程序的人就兴奋起来了,就会很高兴地去发明些什么,这种内在激发的驱动力一爆发,就造成了现在 jQuery 插件遍天下的格局。这和 Rails 本身敏捷而富有艺术性的气质所带来的全球影响力的爆发是一致的。

不过这带来了一个问题。插件满天飞,怎么保证质量和兼容?自由带来的多样性是件好事,太多了就分不清楚谁是谁了。虽然 jQuery 提供了统一的 svn 仓库,不过各式的插件的文档,测试,演示等等,各自为政,颇为凌乱。或许需要一个类似 CPAN 一样的统一管理介质?

jQuery 对 DOM 对象的操作很容易--主要是很舒服,可要处理数据的时候,就不那么舒服了。我总是对 prototype.js 中的 inspect(), join() 念念不忘,可 jQuery 没有,好不容易看到一个 Array 的插件,提供的功能也是捉襟见肘。所以我目前的结论是,对页面元素的控制上,用 jQuery,对数据的处理上,用 Prototype.js 。

还好,jQuery 只是在自己的命名空间里面东奔西跑,按照标准的方式,两者可以共存。不过 jQuery 为了 write less,默认情况下,将自己的类捆绑到 $ 函数上,于是,这一强大的内激效应让一些插件的作者忘掉了这只是一个快捷方式,只要在插件内部也使用这种写法,就会和 prototype 冲突。比如说:ThickBox 3.1

开会去了…

LinuxMCE 安装包掠影

LinuxMCE 是一个类似 Widows 的 Media Center Edition 的软件包。目的是提供免费的集成的家庭媒体中心。目前只能安装在 Ubuntu 6.10 的系统上。完成安装后你的计算机就可以像一台超级电视机一样,上电就可以用。当然它的功能不局限于播放媒体文件,貌似是要整合所有的家电,让你的家成为智能住宅。

但是很遗憾,我没能安装起来,看上去可能是安装脚本的问题。不过这个不是我要说的,一番好奇,展开了下面的探索之旅:

安装需要一张 Linux MCE 的光盘,并下载一个 installer 的包。安装这个 deb 包之后,在桌面上就出现了 Install Linux MCE 的快捷方式,图标是一个红色的光盘。其实说快捷方式不够准确:

lrwxrwxrwx 1 root  root  46 2007-03-27 16:25 mce-installer.desktop -> /usr/share/mce-installer/mce-installer.desktop

显然,这个安装包的东西都放在了 /usr/share/mce-installer 下面:

$ cd /usr/share/mce-installer/
$ ls
mce-installer.desktop  mce-installer.sh

为什么桌面的那个图标显示那样的光盘,这是因为这是一个 Gnome 的配置文件:

$ cat mce-installer.desktop
[Desktop Entry]
Version=1.0
Encoding=UTF-8
Name=Install Linux MCE
Comment=Install Linux MCE Media Center
Exec=gksu /usr/share/mce-installer/mce-installer.sh
Icon=/usr/share/pixmaps/gnome-cd.png
StartupNotify=false
Terminal=false
Type=Application
Categories=GNOME;Application;Audio;Music;Player;AudioVideo;X-Ximian-Main;X-Novell-Main;X-Red-Hat-Base;

所以以后自己也可以做这些类似的事情。我记得 windows 下面的快捷方式也是类似这样的配置文件。

再来看那个 mce-installer.sh 。运行的时候它会开始一个图形界面的窗口向导,这是怎么做到的呢?我知道 GUI 的部分应该是 Python 写的,不过这个脚本展示了一些我原来不了解的东西。先来看看它的内容:

#!/bin/bash
 
exec &>/dev/null </dev/null
export DISPLAY=:0
echo ""
echo "Linux MCE Installer Starting ... please wait"
echo ""
 
# create a temp directory to extract to.
export WRKDIR=`mktemp -d /tmp/selfextract.XXXXXX`
 
SKIP=`awk '/^__ARCHIVE_FOLLOWS__/ { print NR + 1; exit 0; }' $0`
 
# Take the TGZ portion of this file and pipe it to tar.
tail -n +$SKIP $0 | tar xz -C $WRKDIR
 
# execute the installation script
 
PREV=`pwd`
cd $WRKDIR
./mce-installer
 
# delete the temp files
cd $PREV
rm -rf $WRKDIR
 
exit 0
 
__ARCHIVE_FOLLOWS__
...

最后省略号的部分是二进制的数据,而 __ARCHIVE_FOLLOWS__ 表征分割线,见该行:

SKIP=`awk '/^__ARCHIVE_FOLLOWS__/ { print NR + 1; exit 0; }' $0`

$0 就是这个脚本自己的文件名,用 awk 取出匹配分割线的这一行,然后输出下一行的行号,行号保存到 $SKIP 变量中,然后呢:

$ tail -n +$SKIP $0 | tar xz -C $WRKDIR

它用 tail 命令取出自己脚本中第 $SKIP 行及其后的文件内容,通过管道转给 tar 命令解压缩到指定目录。这时候我知道原来后面的数据是一个 .tar 压缩包。为什么不用一个独立的外部的 .tar 文件呢?我想单一文件的安装程序更方便,也更酷,就像 .exe 或者 .msi 文件一样。

那个安装目录 $WRKDIR 早在之前就定义好了:

export WRKDIR=`mktemp -d /tmp/selfextract.XXXXXX`

不过看起来有点奇怪,目录名称中的一串 X 看上去像是模板,仔细往回看,原来是用 mktemp 创建目录,没见过的命令,查 man 文档:

mktemp -- make temporary file name (unique)

所以这里的 temp 不是 template 而是 temporary ,不过所要创建的文件或者目录确实可以用模板的形式:只有末尾的连续 X 才被替换为随机的字串,目的是要创建一个不重名的临时文件或者目录。不错,又学了一招。

接下来的事情毫无悬念,到那个安装目录, 执行真正的安装程序。完成之后删除那个临时目录,结束。

看起来很简单,不过着实满有趣的,略富技巧性的实现。

设置 gnome-terminal 标题

虽然有 screen 可以用,不过在本地机器上还是喜欢开终端窗口。为了区别,我使用 Menu 键快捷设置标题。但是每次都去手工做这些事情,未免繁琐。我希望每个终端在做特定的事情的时候,自动给自己设置一个标题。参考这篇邮件之后,大致归纳如下:

在 bash 下面:

export PROMPT_COMMAND='echo -ne "\\033]2;Chunzi\\007"'

在 sh 下面:

echo "\\033]2;Chunzi\\007"

坦白说,我不知其所以然,管用就好。看上去回显一段带有控制符的文本就行。也不会影响当前的 shell 提示符。什么时候了解了内中机理再说。

bash -i + tee vs. script

之前我用 script 命令记录 shell 中的输入输出数据,或者到一个普通日志文件,或者到一个管道文件。

重新阅读了《BSD HACKS》Hack #11 之后,这才意识到,script 实质上是重新创建了一个新的 shell 。所以类似的有:

$ bash -i 2 > &1 | tee shell.log

-i 参数使新的 shell 按交互模式(interactive)启动,然后将 STDERR 重定向到 STDOUT,再通过管道把 shell 的输入(我们在 shell 下键入的)输出(命令输出的信息和错误信息)都传给命令 teetee 是个简单而有用的小工具,它接受输入,并同时输出到文件和 STDOUT。所以这里 tee 一面让我们看到 shell 的交互情况,一面悄悄地把它们记录在案。自然,shell.log 就是保存的日志,可以在另一个窗口中实时观看变化:

$ tail -f shell.log

很好玩吧~

代码进化

我现在的工作内容之一是写 Anti-Spam 规则,规则保存在若干 .cf 文件中,每个规则都必须有个唯一的名称。发 Spam 的人知道你会写规则,所以有些关键的地方,他们都会尝试多种变形以逃过滤网。而取规则名的时候按意思或者元信息,所以不经意间就会出现规则名重复。尽管不多,但总不好。

四天前,我写了一个 one-liner :

$ less *.cf | awk ' $0 !~ /^#/ && $0 !="" {print $2}' | sort | uniq -c | sort -nr | awk ' $1 > 1 {print}'

less 读取当前目录下的规则文件,然后给 awk,找到不是注释的行,也不是空行的那些,打印第 2 列,也就是规则名,然后排序,再用 uniq 计数把重复次数追加在行首,然后再用 sort 按照数字倒序排列,最后取出数字大于 1 的,也就是有重复的那些规则名打应出来,包括重复计数。

这两天翻过《BSD HACKER》 之后,就觉得这个好罗嗦,于是重写:

$ sed '/^#/d;/^$/d' *.cf | awk '{ print $2 }' | sort | uniq -dc

直接用 sed 读规则文件,将注释行删除,将空行删除,然后给 awk 打印规则名,给 sort 排序,然后让 uniq 只输出重复的行并计数。

怎么说呢,这就是知道什么不知道什么的差别。开始我愚蠢的用 less 传文件内容,这是思维定势,自己总用 less 看文件,就觉得程序也该用 less 看文件。uniq 的 -d 参数一下子把最后一段罗嗦的 awk 代码斩掉。原先用 awk 的模式来找规则行,不如 sed 的两个替换干脆明快,两个 d 命令让我觉得很爽。