Shell Tips: cppath、scppath、mybackup

分享几个觉得有用的小 shell 函数。

1. scppath

在进行一些跨机器的操作时,每次 scp 总要手动去拼那个路径,首先从 PS1 拷贝粘贴用户名和主机名,然后再 pwd 拷贝粘贴当前目录,然后再 ls 拷贝粘贴要 scp 的文件名。好烦啊,所以就写了下面这个小函数来生成 scp 的文件路径,放到 ~/.bashrc 里。

function scppath()
{
    local _IFS=$IFS
    IFS=$(echo -en "\n\b")
    local _file
    for _file in $@; do
        echo "\"$USER@$HOSTNAME:$PWD/$_file\""
    done
    IFS=$_IFS
}

2. cppath

同样可以有 cppath。

function cppath()
{
    local _IFS=$IFS
    IFS=$(echo -en "\n\b")
    local _file
    for _file in $@; do
        echo "\"$PWD/$_file\""
    done
    IFS=$_IFS
}

3. mybackup

这个函数是偷懒备份用的。当写代码写到一半,不想或者不能 check in,但又想备份一下时,就用这个命令对文件或者目录进行自动的备份。

function mybackup()
{
    local _bak_dir=~/history
    local _path=''
    mkdir -p $_bak_dir
    local _IFS=$IFS
    IFS=$(echo -en "\n\b")
    for _path in $@; do
        if [ -f $_path ]; then
            cp $_path $_bak_dir/"$_path".`date +%Y-%m-%d.%H-%M-%S`
        elif [ -d $_path ]; then
            _path=`basename $_path`
            tar -cvf $_bak_dir/$_path.`date +%Y-%m-%d.%H-%M-%S`.tar $_path
        fi 
        echo "Backuped $_path to $_bak_dir."
    done
    IFS=$_IFS
}

编程杂感 20110313

唉,最近表达的欲望很小,这篇日志也仅仅是凑数而已。前一段时间周旋在几个项目之间,忙的没什么时间思考问题或者写字。从上周开始,退出了一个跨部门合作的项目,专心于自己的事情。其中的原因有很多,不好说也不可说。

这半年来,对我所负责的系统,我致力于的是消灭各种 bug,提高稳定性,添加新功能以及为系统的未来发展做一个较为长远的规划。这半年里的程序升级要么从非常微小之处着手,要么是新的模块,擅长的是以最小的代价修复一个具体问题,但不曾仔细思考过如何对一个模块的设计缺陷进行逐步的全面的修正。这种修正类似于重构,但不是推倒重来,而是慢慢地逐步演进。我现在感觉到,在这方面的能力或经验,我还是缺乏的。

这样看来,被分配去做一个已有系统的维护和升级也不是坏事。最近可自己支配的时间多了些,我就沉浸在代码的阅读里。常常在思考的问题是如果让我来,这部分代码的结构该如何设计,才能够提供最大的灵活性,例如易复用、易扩展。好的代码能让你赞叹,差的代码也让你嗟叹,对已有代码的反思和修正,未始不能让自己得到成长。在这个方向上,我和我们的项目都还有很多功课需要做。

开始学车

我终于又开始着手实现今年的一个愿望:学会开车。

报的是北京靠南的一家驾校,东方时尚。因为这个驾校离我家很近,班车的话只需要半个小时,而且也是 Ironfeet 同学强烈推荐的一家。从这两天的体验来看,还是挺正规靠谱的。

这个周末是交规学习。我知道很多人不愿意专门花时间去听交规课,认为看看书就足够了。但是我觉得完全是为了安全的考虑,新手去听听培训还是很有必要的。当然如果有老司机指点的话,看书的效果也不一定比上课差。不过说实在话,这两天的课听下来真的挺累的,从早九点到下午四点半,几乎是连轴转。我有一次课间 15 分钟居然都睡着了,而且还做了个梦……

上完课最大的好处就是:总算认得各种交通标志、标线了!让我感叹的是,这些东西在我身边环绕了那么多年,居然都没有注意去识别过。我就像一个生活在魔法世界的 Muggle!

有没有这样一种手机应用?

Google 的 Jeff Dean 在演讲中提过:对一套系统来说,每年典型的事故率会是这样的:1. 1-5% 的硬盘会坏掉;2. 全系统宕机至少两次。以前没有深切体会,现在我会说,i cannot agree more!

当你负责的系统线上服务器达到百台以上规模时,你就会发现这个系统频繁会出各种各样的问题:死机,是最频繁的,会有各种各样的原因导致死机,内存占满、CPU耗尽、硬盘故障,还有你永远不知道的原因;硬盘挂掉,如果对没有做 RAID 的系统来说,这是一个灾难,对于做 RAID 的系统来说,这是一个事故,不过也有可能是一个灾难;文件损坏,在脆弱的硬盘上面也是有可能的;存储空间耗尽,未必是所有空间都耗尽,但可能程序问题导致某个分区被写满;数据流延误,作为一个系统总有上下游吧,只要有一个地方卡住了,下面的数据流也就停了。

所以我想任何一个互联网企业都会支持异常监控报警机制,最典型的做法应该就是邮件和短信。虽然我不是运维人员,但是自己写的系统出了问题,也脱不了干系。于是报警短信就成了生活旅行必备之调剂品。

但是,报警多了也郁闷啊!为了尽早地发现系统问题,往往一台机器上会布多种监控,CPU、内存、硬盘、进程、数据流、文件,乱七八糟的一个不能少。这样以来误报率就很高,比如CPU IDLE可能一下子压到0但是迅速回升了,这没啥问题,但是报警短信是照发不误。更别说服务器多了,真出事儿的可能性也大,所以每天接个几条到几百条短信,都是很正常的事情。

让人郁闷的不仅仅是报警短信多,还有和平时短信分不清。时间长了,有些报警会出现在什么时候、会以什么样的频率出现,自己心里都有底了。有的短信不看,光凭规律就知道是啥问题,应该以什么优先级处理它。但是掺杂进平时短信就不一样了,这规律就被打乱了,只好每条必看。在无奈之下,现在我只好用一个手机专门收报警短信。

说了那么多,我就是想知道:现在智能手机那么多,有没有哪款智能手机或者APP,像邮件客户端一样,支持根据短信特征将短信分发到不同的文件夹中,并且可以给不同文件夹分配不同铃声或者干脆没铃声?

居大不易,找由头聚聚

现在有一大感慨:工作以后,想维持一个稳定的博客更新频率真不容易。按照“每周一篇”的愿景来看,这月我还欠着两篇。我写博客一直以来有一个宗旨——记录自己的成长之路。生活也好,技术也罢,总是一点点向前挪。我想在这挪动的舟上,刻点儿或深或浅的印痕,以供日后抚摸岁月的参差斑驳。

北京是个大城市,至少对于我而言。我每天 7 点 45 分准时出门,从南 2 环的牛街赶往北 5.2 环的百度大厦,路上要花费 1 小时 10 分钟,然后在 8 点 55 分进入地下一层的食堂,赶在食堂关门之前打一份早饭。生活的钟摆每天要在北京这个平面上摆动约 46 公里。

在这样一个城市里,维持朋友圈子是一件很困难的事情。在我家那个小县城,打电话叫朋友踢球或者吃饭,聚齐的话也就是十几分钟的事情;在这里,或许需要两三个小时,且极有可能部分人因为堵车、距离太远而不能参加。这大概也是如此多年轻人成为宅男宅女的原因。

庆幸的是还有同学愿意挑战一下这种状况,例如发起号称“吃遍驻京办,逛遍博物馆”的活动。在这一光辉口号的指引下,一个多月后的2月20日,我们终于吃了第一个驻京办!其实是新疆驻京办院里的西域饭庄,托 3G 手机的福,我们在面对院里 3 个饭店犹豫徘徊时,点评网上的“别名: 新疆办事处饭店”让我们决定了去吃这家。

鉴于某人携带单反相机但两块电池皆为耗尽状态的乌龙事件,我只好用华为家的小破 U8220 手机勉为其难记录下此盛事。以下为几样特色菜品:

新疆大盘鸡,味道还行
新疆大盘鸡

羊肉串,感觉比别地儿吃的汁多肉嫩
羊肉串

老酸奶,挺好喝的
老酸奶

剁椒羊排,那是真肥!
剁椒羊排

凉拌羊杂,很有味道
凉拌羊杂

PS:其实这篇文章本应在上周末完成,被各种忙拖到了今天。昨天也和两个研究生同学一起打了场羽毛球,吃了顿饭。我想说的是,虽然北京有各种大,我们有各种忙,但是朋友们还是应该找各种由头多聚聚——老宅在家里会生各种病的,就比如身体各种差的我。 :)

Infobright 数据仓库

最近有部分工作涉及到了 Infobright 数据仓库,就浏览了一些相关的资料,感觉很受启发。下面写一些感想,如有谬误,还请指正。

简单的来讲,Infobright 主要有下面的一些优点:

1. TB 级的数据存储和高效查询。大数据量存储主要依赖自己提供的高速数据加载工具(百G/小时)和高数据压缩比(>10:1),高效查询主要依赖特殊设计的存储结构对查询的优化,但这里优化的效果还取决于数据库结构和查询语句的设计。

2. 高数据压缩比,号称一般能够达到 10:1 以上的数据压缩率。高数据压缩比主要依赖列式存储和 patent-pending 的灵活压缩算法。

3. 与主要 BI 分析工具的兼容性。兼容性这点主要依赖与 MySQL 的集成,作为 MySQL 的存储引擎自然地能够保证与 BI 分析工具的兼容。

除了上面的优点外,它也有一些限制:

1. 不支持数据更新。这使对数据的修改变得很困难,这样就限制了它作为实时数据服务的数据仓库来使用。用户要么忍受数据的非实时或非精确,这样对最(较)新数据的分析准确性就降低了许多;要么将它作为历史库来使用,带来的问题是实时库用什么?很多用户选择数据仓库系统,不是因为存储空间不够,而是数据加载性能和查询性能无法满足要求。

2. 不支持高并发。虽然单库 10 多个并发对一般的应用来说也足够了,但较低的机器利用率对投资者来说总是一件不爽的事情,特别是在并发小请求较多的情况下。

3. 没有提供主从备份和横向扩展的功能。如果没有主从备份,想做备份的话,也可以主从同时加载数据,但只能校验最终的数据一致性,这会使得从机在数据加载时停服务的时间较长;横向扩展方面,倒不是 Infobright 的错,它本身就不是分布式的存储系统,但如果把它搞成一个分布式的系统,应该是一件比较好玩的事情。

在架构方面,Infobright 给我展示了不少新想法,算是受益颇多吧。首先是按列存储,然后把列数据切成小块(Data Pack),进行压缩和统计(DPN, Data Pack Node),然后再对多块数据之间进行知识关联(Knowledge Node),最后对整个表形成知识网格(Knowledge Grid)。虽然说 Infobright 没有提供索引结构,但它 Knowledge Grid 中的 Numerical Histogram、Character Map 和 Pack-to-Pack 结构,怎么看都和 bitmap 索引脱不了关系。只是它的组织形式不像传统数据库中的索引罢了。

其实我们在设计类似的分布式表格系统时,也可以实现类似于 Knowledge Grid 的结构。这个结构未必跟 Infobright 的一样,但是如果在压缩的基础上,基于系统查询模式(分布式系统的查询模式一般相对简单,复杂的也做不来),存储一些辅助的块统计信息以及块之间的关联信息,对于减少查询的资源消耗,提高查询效率会非常有帮助,这也正好是针对分布式表格系统很难建立索引这一缺点的弥补。

参考链接:

这篇文章对 Infobright 及其安装方法进行了基本介绍,最后的一个查询速度对比有些夸张(105:1),我觉得这可能跟查询条件正好能匹配上 Knowledge Grid 中的信息所致;这个博客很有趣,从 2010 年 3 月 8 日到 5 月 8 日之间的文章全是 Infobright 相关的,写的还是挺详细的;Brighthouse: An Analytic DataWarehouse for Ad-hoc Queries 是一篇相关的 08 年 VLDB paper;此外官网上的白皮书不能直接下载,但在搜索引擎中能搜到一些。

我的2011愿望清单

在公司 2010 年会上看到《盒子里的梦想》视频,我想,列一个清单挺好的,像 KPI 一样,能督促自己达到一个明确的目标。

以下愿望排名不分先后,可能还会再添加:

  1. 请以前带我的经理 Tony 吃顿饭。我的第一份工作是 Tony 给的(ci123.com的几天不算),他也相当地照顾我。三年没见了,不知道他现在怎样,很想念,想一起叙叙旧,喝喝酒。

  2. 骑车爬 10 次香山,去 1 次天津。山地车已经买了,放那生锈就太浪费了。

  3. 体重降到 135 斤,甘油三酯和胆固醇回到正常水平。

  4. 要快乐,要宽容,多帮助别人,与人交往多看优点,少计较。

  5. 资助 1 个孩子读书。

  6. 逛 10 家博物馆。

  7. 学会 1 门编程语言。

  8. 掌握 SEM 和网站分析。

  9. 每周写 1 篇博客。

  10. 学会游泳。

  11. 学会开车。

  12. 学会唱 5 首歌。

再练滑雪之南山

再练滑雪之南山

这次部门 building 选择了南山滑雪,呃,其实我有些失望。我投的是泡温泉,主要是吸取了上次去坝上的教训,周末不想太劳顿。

好在滑雪我也挺喜欢,而且南山滑雪场也比较熟悉,去年实验室年会去过一次。印象颇深的是它的门票价格,好贵啊!不过今年貌似更贵。

说大厦南门集合,结果车其实停在北门;说 8 点一刻出发,其实耽搁了半个小时;27 个说要参加,结果只来了 20 个。唉,发现在公司组织活动跟大学里一样,都不容易,不靠谱的人太多。本来去的路上发了点儿面包啥的当早饭,进场的时候发现已经10点多,领队干脆决定提前吃中饭——直接结果是大家的战斗力都太差,一桌大餐剩了不少。

这次滑雪主要练了练倒滑,还不错,初级道上摔了两跤就有了些模样。这次也上了几次中级道,已经没啥恐惧感了,只是速度太快,除了滑滑 S 形,基本上练不了啥技巧。后来在初级道上消磨了些时间就撤了。不过心里一直在想,啥时候能有胆量上高级道啊?

回来明显地感觉肩胛骨下角附近的肌肉酸痛,不知道为啥不是胳膊疼。京郊的天比京城蓝一些,路上两边的景色挺索然的。

2010年志

邵小毛《现象 2009 》的旋律似乎还回响在耳边,恍惚间脚步却马上要踏出这个 2010 年了。时光如梭的沧桑感,促使我写点儿文字记录下来这悠悠的一年。

2010 年初,我是幸福的,这幸福完全来源于一封论文的录用函。自我开始研究工作以后,在精神方面的压力非常之大。读过研究生的同学们都知道这压力来源于哪里、来源自谁。这个压力也从 09 年初开始积累,到 12 月底达到顶峰。然后就像过山车一样,嗖一下,这封录用函把我的压力全部释放了。也让我能够安安稳稳地过了个元旦,过了个好年。

春节前,希希的工作也总算确定,和我一样成了流落在帝都的北漂族。要多谢室友宗斌让出寝室,使得在北京找工作的这段时间里,她能躲在我的宿舍里。不过当初我们真的是想尽了一切办法通过看门大爷的监视混入宿舍, :) 。在没有装门禁之前,我让她进出门之前把脖子脸围得严严实实,这样就和同样住一个楼里的女生分不出来了;装了门禁之后,她就先用我的卡刷进去,然后在一个角落的二楼窗口把卡扔还给我。唉,好辛苦啊!幸好我的一个姐姐住的地方离学校不远,后患无忧的我们可以有恃无恐地尝试各种混入宿舍的方案,成功率还是很高滴...

春节回家,就是陪了陪了妈妈和妹妹,当然中间夹杂了各种买年货、走亲戚。随着年龄的增大,能在家呆的时间越来越短,就越来越恋家。不再像之前向往着学校的热闹,离家的时候有许多的不舍。

年后就是各种写论文、改论文、投稿、准备答辩。在一个春光美好的日子,我准备答辩申请被指使得昏头转向时,忽然听说一个大学加研究生师兄在我住的那幢楼上寻了短见。也许这个年轻的生命,也似他溅出的鲜血一般,早已被这个冷酷的社会和制度冲刷得没了生气、不见了痕迹。

流程一样的答辩完后,我在西二旗提前租了间房子。慢慢地把自己的东西搬了过去,也准备把自己的生活慢慢地搬出学校。用了几天,去了趟南京,回了趟家。然后我真的开始了正式的职业生涯——作为一名程序员。

离开学校之后,我又收到了一封国际会议论文录用函,此刻的我,更开心。因为事实再一次证明了,我不是仅凭侥幸毕业的,算是我送还某人的一个大礼。

七月初有一个不幸的消息,我那辛苦操劳了一辈子的妈妈罹患上了乳腺肿瘤。首先在家里动了手术,拿出来肿瘤去做活检,发现有交界性的症状,考虑到妈妈的年龄,后来只好又请郑州的专家做了根治术。第一次手术我没有陪在身边,第二次手术我请假回家陪护了一段时间。看见妈妈那痛苦的样子,我真希望动手术的是我自己。我要非常非常感谢我的妹妹,我不在的日子里,她一直陪在妈妈身边。如果没有她,妈妈不能恢复得那么好。妹妹,你辛苦了!

八月初有个好消息,原本计划在南京分部轮岗半年左右的希希被提前调回了总部。于是我那 9 平米的小房间,显得狭窄了很多。两个人单位的距离,大概也能横跨北京城了。在花费了两个星期找房子之后,最终找到了法源寺附近的一个一居室。于是我的上班路程,由十分钟变成了一个小时,但我觉得挺幸福的,总算有了两个人单独的温暖的小窝。其实八月份还有一个意外的好消息,只是鉴于公司政策不能泄露,就不说了。

九月份我才能渐渐地专心工作,日子乏善可陈。值得一提的大概就是随部门组织到所谓的坝上草原玩了一趟,其实失望比满足更多。

十月与九月没有太多的不同,我买了个 Kindle 大概算是件新鲜事。从此阅读量大大地增加,以小说为主,在上下班的路上看了不少。

十一月有了一些变动,一个是我在这个世界上摸爬滚打又转了一轮,满 24 岁了;另一个是我所在的团队所有的人都面临着一个抉择,是去新成立的一个部门,还是留在现在的部门。我从一开始就决定了留在现在的部门。解释呢,我能给出一万种。但大哥你是了解我的,人生在世,除了功名利禄,还有情义二字。哦,这个月里,我还买了辆山地车,只是可惜,到现在只骑出去了两次。

今年冬天,也是很冷的。十二月初我带希希初次尝试了一下滑雪,其余大部分时间,还是宅在家里。为此我又入手了台电视机和一个 Wii 游戏机,两个人在家里玩,蛮欢乐的 :)

工作以后的整整半年,我没有仔细地照顾这个博客,尤其是技术方面的分享更少。虽然在这段时间里,我做了不少事情,但却不知从何写起,什么能写。在知识方面,零零散散,我目前还是欠缺一些全局的思路。对应于此的是博客访问量的大幅下降,这是理所应当的事情。这一点上,我希望新的一年会有所改观。

这半年里,在技术积累方面,我调研了不少分布式的存储系统,也参与完成了一个分布式系统的总体设计——虽然由于种种原因其最终可能会被改得面目全非;在工程方面,对目前正在使用的一个系统进行了几次技术升级,解决了关键的几个稳定性问题,使系统的可靠性大大增加。我自认为尽心尽力地完成了不错的工作,随之而来的是工作内容和责任的增加。这对我来说是个挑战,但是我更喜欢它带来的机遇,所以肯定会努力去做。

在公元 2010 年,我有不少坎坷和跌撞,也有很多经历和成长。总的来说,在我坐在电脑前回首时,这一刻,我感觉是幸福的!

在 shell 脚本里打日志

今天小弟在重构代码中的一个脚本模块,其中涉及到日志功能。上午花了点儿时间想出了个在 shell 打日志的技巧,觉得值得写一下。

希望要实现的效果是:实现一个 write_log 命令,给一条出错消息作为输入,write_log 记录日志时自动加上 时间戳、脚本文件名和行号。形如:

2010-12-17 19:13:44 [work.sh:24] FATAL: mkdir -p /x.

时间戳、脚本文件名都好获得,但是行号就没那么容易实现了。shell 中的 $LINENO 变量只能展开成当前行的行号,如果把 write_log 实现成函数的话,势必在函数中无法使用 $LINENO。

开始我想了好大一会儿,觉得 eval 能干这个事情。但是如果用 eval 的话,还不如直接把 $LINENO 传给 write_log 函数呢,与我的初衷不是太相符。我拉来同事讨论了一把,也没解决问题。正当我准备放弃了,计划每次传 $LINENO 参数时,忽然想起来,怎么把 alias 给忘了呢?

于是,write_log 的实现就是这个样子了:

function _write_log()
{
  if [ $# -eq 2 ]; then
    if [ -z $LOGFILE ]; then
      echo "$(date "+%Y-%m-%d %H:%M:%S") [$0:$1] $2"
    else
      echo "$(date "+%Y-%m-%d %H:%M:%S") [$0:$1] $2" >> $LOGFILE
    fi
  elif [ $# -eq 1 ]; then
    if [ -z $LOGFILE ]; then
      echo "$(date "+%Y-%m-%d %H:%M:%S") [$0] $1"
    else
      echo "$(date "+%Y-%m-%d %H:%M:%S") [$0] $1" >> $LOGFILE
    fi
  else
    return 1
  fi
}
alias write_log='_write_log $LINENO' # 这里必须使用单引号

存在的问题是:上面这段代码在 bash 里是不工作的,但是用 sh 可以——即使 sh 也是链接到 bash 的。问题出在 alias 上,可以把问题简化成这样,有一个脚本 a.sh:

$ cat a.sh
alias lss='ls -l'
lss /tmp

这个脚本用 /bin/sh 执行是这样的:

$ sh a.sh 
total 8
drwx------ 2 gdm gdm 4096 2010-12-17 19:34 orbit-gdm
drwx------ 2 gdm gdm 4096 2010-12-17 11:04 pulse-PKdhtXMmr18n

用 /bin/bash 执行是这样的:

$ bash a.sh 
a.sh: line 2: lss: command not found

把 bash 随便 link 成一个叫 sh 的链接文件,再执行是类似这样的:

$ ln -s /bin/bash ~/sh
$ ~/sh a.sh 
total 8
drwx------ 2 gdm gdm 4096 2010-12-17 19:34 orbit-gdm
drwx------ 2 gdm gdm 4096 2010-12-17 11:04 pulse-PKdhtXMmr18n

这个问题肯定是有原因的,我不愿意去翻 bash 源代码,也不知道哪里去找答案,所以我放弃了,直接在文件头加上

#!/bin/sh

如果哪位兄台知道这种“奇怪”现象的原因所在,请不吝赐教 :)

去八达岭滑雪

由于某人懒惰成性,下班就宅在家里看电视看小说,上周末我趁着团购的东风,成功地把她拉到八达岭滑雪场溜了一圈儿。但没料到的是,最后被溜的是我——每周四我都有羽毛球活动,于是那天胳膊腿儿都酸疼。她反而是精神奕奕,出了滑雪场还想拉着我跑步到公交车站,那可是有两三公里啊!

玩耍的过程暂且不提,这来回的路程可真是遭罪!去的路上倒两趟公交车一趟黑出租,最可恶的是德胜门 919 车站,纵使我去过一次,也分不清楚该在哪里等车。那些穿着公交制服的人也不知道是不是公交公司员工,向几个人问总是能问到不同的答案,让我很崩溃。而且 919 支线很多,分清楚该坐 919 哪趟车大概和在西直门立交桥上辨别行车方向难度差不多。到最后我想明白应该坐 919 支到西拨子,这个决定没做错。

回城时倒不容易坐错车,所谓条条大路通帝都,只要是 919 上了方向就没错。不过不同线路停靠车站的多少有不同,直接导致了行车速度的不同。回来时因为 919 支末班车已过,上了个 919 大站快车,速度那叫一个慢啊!再加上堵车,走了 3 个多小时才到德胜门,回到家已经 8 点半了。路上还没座,本来滑完雪活蹦乱跳的希希一路累得连说话的力气都没了。

在北京几年的一个很大的感触总结起来就是“长安居大不易”。中国那么多聪明人,为什么把首都设计成了这样一个“堵城”呢?

我的新玩具

美利达公爵 600

话说我平日里老是心里默默计划着周末要做啥做啥,仿佛一副上进好青年的模样。但是真的一到周末,做的事总是和预期大相径庭。有些人大概会循循善诱:业精于勤荒于嬉,少小不努力老大徒伤悲,程序员是个吃青春饭的职业要随时注意学习新知识...可是哥,这些小弟都知道。

因为要过生日了,xixi 送了我一辆山地车作为生日礼物,现在成了我的新玩具。买来已有两个星期,可惜未曾有时间出去骑,今天总算忍不住出去转了一圈,在二环内瞎晃悠了四十多公里。其实以我的体能和技术,现在还配不上这辆车,但是我听从 Knuth 爷爷的教导,“过早优化是万恶之源”,就让优化来得更早更猛烈些吧——最好先来套我也配不上的房子!

话说自从毕业以后,我的身体状况也差了很多。首先是变肥了,重了将近十斤,然后慢性病又开始困扰我了。久病自成医,从上周三开始,我就知道慢性鼻炎又该爆发了,我能够逐步地感觉到它进行到了哪个程度。只是由于工作原因没有及时去看病或买药,导致还是进行到了挺难受的地步。从今天的状况来看,应该是控制住了,我期望它明天能够有所好转。

我把身体上的很多问题归咎于锻炼不能坚持和饮食保暖小细节的不重视。其实我也有注意运动,只是没有毅力,其中也有工作时间上的客观原因。夏天的时候还学了两次游泳来着,天冷就没去;每周四晚上部门有羽毛球活动,天一冷穿得多不想出汗,再加上活动时间的确很晚,因而现在就经常旷掉;公司的健身房,其实我也是去过几次的...

自行车这种东西,技术要求不是特别高,一个人也能玩,时间也较随便,而且是一次性投资,希望我能玩长久点儿吧。其实我心里还是有些小野心的,要是能学成点山地车技术,能够在真的山地搞一把就再好不过了,只是目前还仅限于体能锻炼阶段...

上个月体检,检查出了我甘油三酯和胆固醇偏高,让我小害怕了一下。现在饮食较以前注意多了,油炸食品不敢吃了,好不容易适应口味了的肥肉也不敢吃了,但食堂里偏清淡的菜肴又少又不好吃,搞得我很是被动,唉!

上周末恰逢农历十月一,是我老家所谓的“鬼节”,我就趁机回了趟老家,看看病后初愈的母亲,给老爹上上坟。都说物是人非,可是那家乡的物,也渐渐地模糊了痕迹,对于离乡多年的我,剩下的只是些熟悉的感觉。人长大,走着想走或者不想走的路,再回首从前,难免觉得悲戚。但就像最近火爆的短片《老男孩》那样——C'est la vie!

Linux screen窗口中文乱码问题

环境:Linux Dist: CentOS 4.3,locale: en_US.UTF-8, .vimrc: set fencs=gbk

目标:终端使用 less/more/grep 等命令正确显示 GBK 编码文件内容,vim 正确显示 GBK 编码文件汉字

症状:

1. 系统自带 gnome-terminal 在设置终端编码为 GBK 后,能达到目标。

2. 使用 xshell 在 windows 平台上设置终端编码为 default 时,ssh 登录到 CentOS,能达到目标。

3. 在 screen 命令窗口内,无论终端还是 vim, 中文均显示为乱码,无法达到目标。

解决办法:在 ~/.screenrc 中,添加下面两句:

defencoding GBK
encoding UTF-8 GBK

我的猜测是 xshell、gnome-terminal 等终端能够将自身编码传给系统,因此系统能够对输出自动进行转码。而 screen 属于终端中的终端,它自身的编码不是 GBK,导致传给系统以后没有对输出进行转码。设置 screen 的编码和转换规则后,就 OK 了。

新购入姓名拼音域名一枚

昨天看到某同事使用姓名拼音作为域名,于是手贱 whois 了一把,居然发现 yangwenbo.com 被人放弃了,所以我理所当然地拿了下来。现在 yangwenbo.com 直接指向我的博客。

我的旧 .cn 域名本应在 2010 年 10 月 29 日过期,我也没有继续续费的打算。但现在该域名 whois 查询时间延期到 2011 年,显示注册人仍然是我,而且仍然解析到我的网站,我不知道什么样诡异的原因造成了这一现象。但可以确定的是该域名已经不属于我,我也无法对域名进行管理,因此请不要再使用 .cn 域名访问我的网站,我也静待搜索引擎的 .cn 记录过期。

入手 Kindle

入手 Kindle 有一个多星期了,下面写点儿经验,希望对别人有用。

关于购买:

买 Kindle 3 (wifi) 的想法上个月就有了,犹豫了很多种购买方式,后来决定在淘宝上下手。9 月 21 号在五人行数码下了个单,但据说要很长很长时间才能拿到货(排队有 400+),就和店家协商退钱了事。就这样一直等到十一国庆假期,也没有找到合适的商家。国庆期间在豆瓣上看到一家说有现货的,国庆95折,1299 块,就买了一个。

第二天店家打电话说她的 Kindle 无法注册,问我还要不?我没有犹豫很久,就说还是要吧。第三天货到了,又联系店家,店家说愿意退款 100 作为无法注册的补偿。这样一想,还不错哦,就付款了。相当于 1199 买了个无法注册的 Kindle 3 wifi 版。

结果第四天就发现论坛上关于这批无法注册 Kindle 的话题已经热起来了,各种争论不休。大部分商家的做法是低价销售无法注册版的 Kindle 3,普遍降一百到两百不等。最便宜的是我在 Twitter 上看到的一个店家,卖到 1050,和我的入手价差 150 呢。唉,怎么说呢,心中还是有点儿小郁闷,要是再多观望那么两三天,就好了——不过这也是无法预料的事,不提了。

关于皮套:

我买的不是 Kindle 3 专用的皮套,是一般 7 寸电子书的皮套,图便宜。用起来感觉还行吧,反正主要是起一个保护屏幕的作用。

关于阅读:

各种评测啥的在hi-pda、多看等论坛上能找到很多,我就不赘述了。从个人的体验来讲,感觉还是挺值的,看书很舒服。而且我女朋友特别喜欢,已经抱着 Kindle 看完了阿西莫夫的《基地》系列。我主要是坐地铁时候用,也看了两本书:《民主的细节》和《1Q84》。

关于多看:

我尝试了一下多看系统,后来恢复出厂设置给删了,就没有再装。从个人来讲,我对多看系统主要有两点意见:1、对 mobi 格式支持的不好(封面、分节等显示不太好);2、没有 web 浏览器。虽然多看对中文 txt 支持较好,但是我用 mobipocket reader 转一下 txt 到 prc 格式也不复杂(想让 mobipocket reader 支持中文 txt 分段一般需要加空行,一句命令 sed 's/$/\n/g' file 就可以解决)。剩下对我来说,貌似没有什么用多看的理由——哦,中文输入法可能算是一个,以后再说吧。

关于 hack:

使用原生系统的话,还需要做些 hack 工作,主要有两点:1、修改 locale 为 zh-CN(可以加 .utf-8 或者 .gbk,如果想看 txt 的话),方法很多论坛上有流传;2、无法注册版本需要越狱一下,才可以支持显示用户名、创建 collection、修改时间等操作,俗称“假注册”,越狱方法见这里

关于图书管理:

图书管理的话,我用的是非常原始的方法:同步目录。在电脑上建一个 kindle 目录,使用 synctoy 将这个目录与 kindle 根目录同步。这样我下完图书就扔到电脑上的 kindle 目录里,连 kindle 的时候打开 synctoy 同步一下即可。

此外有一些比较好的图书管理软件,比如 calibre,但我觉得太重量级了。不过 calibre 还是有用处的,比如抓取新闻和 web 服务器功能。比如想看《南方周末》时,用 calibre 抓取下来做成 mobi 电子书,然后在 kindle 上用 wifi 访问 calibre 启动的 web 服务器,将做好的电子书直接下载到 kindle 里,少了 usb 连接的麻烦。

哦,越狱还有一个好处,可以获得 kindle 系统的 root 权限,当然也可以 scp 拷贝文件了,而且还有 vi、gdb、ssh 可用哦!里面看起来就是一整个简化过的 Linux 操作系统。

Linux kindle 2.6.26-rt-lab126 #5 Wed Sep 15 19:25:13 PDT 2010 armv6l unknown

关于图书资源:

我测量了一下,Kindle 3 wifi 的屏幕尺寸大约是 90mmx122mm,如果自己做 6 寸的 pdf 电子书的话,最好将页面大小调整为90mmx110mm,这样下面留12mm来显示阅读进度,pdf就不会变形很多了。 hi-pda 论坛的 e-ink 版面有很多资源(电子书、配置方法等),在入手前和入手后都可以常去逛逛。

关于升级 3.0.2:

我刚才升级了一下,感觉还是有点变化的。比如以前 pdf 翻页时候,连续按下一页或者上一页,响应会比较慢,升级之后速度会快一些。剩下的改进还有待观察。

《情定日落桥》

这是一部老电影,也是一部好电影。

好久以前一个朋友推荐过的,好久以前就下载到硬盘里静静地躺着,今天才翻到,想起了它。于是吃着煮的泡面,开始看起……

两个小朋友令人叹羡的爱情,再加上青春无敌的明媚表情,一下子把我等俗人击打得伤心疾首。苍天啊,大地啊,为什么我没有生在 1970 年代?

可惜的是,即使生在七十年代,在这个国家,我们恐怕也只能像静秋和老三那样,面对着种种丑恶,将一丝丝的小感动,藏在最不为人知的地方,断不能暴露在那污浊中。

我们无法回到三十年前,也无法拥有丹尼尔和萝伦那样与众不同的爱情,于是只能追求在现世中过好自己平淡宁静的小生活。

但是还有一些人,宁愿放弃这种生活,秉笔直书曾经的污浊,努力去涤荡这个仍不清新的世界,只是为了悲惨的过去不再重演,未来人们能生活的更坦然有尊严。

我,只能代表一个仅敢追求自身平淡宁静小生活的懦弱的年轻人,站在这互联网的一个角落里,向这些勇士们致敬,并表示祝贺!

在北京的数学系的娃儿们

既然今天大家都无心工作,咳咳,那我就来更新篇博客吧。

好久没有上传照片,今天来更新张图:

在北京的数学系娃儿们

中秋节前某 CTO 同学号召我们南大数学系在北京的朋友聚会一把。本人由于公司活动缺席了一次 03 级同学的聚会,心想这次要是再不去,岂不是就没人带咱玩了?于是携家眷欣然而至,到了聚会的桌游厅,才发现这原来主力不是 03 级的啊!

03 的哥们好像就来了张博、刘元杰和刘增禄,02 的也就来了一个获鼎,再加上我认识的学弟中洋、程棵和新雨,剩下的就全是陌生人了。

才打了两把三国杀,就到饭点儿了,看来这三国杀要是人多玩起来实在太慢了。于是就去吃饭,于是就喝酒,然后就有了上面这张照片。

从照片上看,好像我的脸又大了一圈。今天早晨去体检,量的体重依稀记得好像到 72 公斤!而且由于最近运动量的缺失,也导致了慢性鼻炎有露头的倾向,这可如何是好啊。看来加大运动量迫在眉睫,水木大神啊,赶紧组织山地车的团购吧。

WordPress博客评论合并工具

上篇,这里共享我写的一个用来合并 WordPress 博客评论的小工具。该工具可以将两个镜像 WordPress 博客上对同一篇文章的评论合并起来。

下面先介绍合并的步骤:

1. 首先到这里下载我修改的 WordPress 导入插件,并按照安装一般 WordPress 插件的方式,安装并启用该插件。

2. 然后在 WP 管理后台选择“工具->导入->WordPress”,然后上传从镜像 WP 博客导出的 xml 文件。

3. 在下一步选择“Only Merge Comments” 很重要!!!

Wordpress博客评论合并工具

4. submit,稍等片刻即可。

其实我没有重新制造轮子,只是修改了一下 WordPress 默认的博客导入工具 WordPress Importer,给它加了点儿功能。只要选中“Only Merge Comments”,使用这个工具是很安全的,它只会将 xml 中与当前博客中存在的文章对应的评论添加上去,而不处理任何不存在的文章,也不会重复添加已有的评论,而且会过滤某些垃圾评论。用这个选项,你可以重复导入很多次 :)

可能的缺陷有:这个工具判断文章是否存在的唯一标准是文章标题,因此如果有多篇文章标题一样,可能会存在问题(未测试)。本人不保证它是充分测试的,因此在应用之前最好还是在本地的镜像测试后进行;如果没有进行测试,请一定在合并之前对博客进行备份

下面是我修改的 patch:

--- wordpress-importer/wordpress-importer.php    2010-06-02 00:38:23.000000000 +0800
+++ ../../www/blog/wp-content/plugins/wordpress-importer/wordpress-importer.php    2010-09-29 19:33:57.953790929 +0800
@@ -49,2 +49,3 @@
     var $fetch_attachments = false;
+    var $only_merge_comments = false;
     var $url_remap = array ();
@@ -258,2 +259,7 @@

+<h2><?php _e('Only Merge Comments', 'wordpress-importer'); ?></h2>
+<p>
+    <input type="checkbox" value="1" name="comments" id="merge-comments" />
+    <label for="merge-comments"><?php _e('Only merge comments, ignore post, tags...', 'wordpress-importer') ?></label>
+</p>
<?php
@@ -483,3 +489,7 @@

-        $post_exists = post_exists($post_title, '', $post_date);
+        if ($this->only_merge_comments) {
+            $post_exists = post_exists($post_title, '', '');
+        } else {
+            $post_exists = post_exists($post_title, '', $post_date);
+        }

@@ -489,4 +499,7 @@
             $comment_post_ID = $post_id = $post_exists;
-        } else {
-
+        } else if ( $this->only_merge_comments) {
+            echo '<li>';
+            printf(__('Post <em>%s</em> not found, comments not updated.', 'wordpress-importer'), stripslashes($post_title));
+            $comment_post_ID = $post_id = $post_exists;
+        } else {
             // If it has parent, process parent first.
@@ -605,3 +618,11 @@
                 // if this is a new post we can skip the comment_exists() check
-                if ( !$post_exists || !comment_exists($comment['comment_author'], $comment['comment_date']) ) {
+                if ($this->only_merge_comments) {
+                    if ( $post_exists && !comment_exists($comment['comment_author'], $comment['comment_date']) && $comment['comment_author'] != 'Unknown') {
+                        if (isset($inserted_comments[$comment['comment_parent']]))
+                            $comment['comment_parent'] = $inserted_comments[$comment['comment_parent']];
+                        $comment = wp_filter_comment($comment);
+                        $inserted_comments[$key] = wp_insert_comment($comment);
+                        $num_comments++;
+                    }
+                } else if ( !$post_exists || !comment_exists($comment['comment_author'], $comment['comment_date']) ) {
                     if (isset($inserted_comments[$comment['comment_parent']]))
@@ -847,5 +868,7 @@
         $this->get_entries();
-        $this->process_categories();
-        $this->process_tags();
-        $this->process_terms();
+        if ($this->only_merge_comments) {
+            $this->process_categories();
+            $this->process_tags();
+            $this->process_terms();
+        }
         $result = $this->process_posts();
@@ -891,2 +914,4 @@
                 $fetch_attachments = ! empty( $_POST['attachments'] );
+                $only_merge_comments = ! empty( $_POST['comments'] );
+                $this->only_merge_comments = (bool) $only_merge_comments;
                 $result = $this->import( $_GET['id'], $fetch_attachments);

悲剧的 MSN Space

MSN Space 总算倒了,所有用户都要求被迁往 wordpress.com,或者下载备份文件。话说我第一个用得顺手的 blog 还是 MSN Space,也用了很长的时间,不免觉得有些悲凉。

今天有同事问我,MSN Space 为什么混到这个地步?我说,本来 MSN Space 还凑合可用,但是每一次改版、每一次改名,都让你觉得更加难用。产品能做到这个份上,也真是不容易,不过无独有偶,MSN 也算得上跟它的绝配了!不知道什么时候能看到 MSN 整体搬迁用户到 AIM 或者改版到 XMPP?

哦,我又忘了,国内还有个飞信呢!不愧是外包给 MSN 做的产品,看看现在的飞信 4.x,我好怀念飞信 3.x 啊!

这件事情让我感兴趣的一点是,我总算看到一个可能,可以将以前在 MSN Space 上的评论,合并到现在的博客中了。或许需要自己写个小工具,假期可以尝试做一下。