2007年08月08日

看了这个视频,感觉不到任何乐趣,只看到愚蠢、低级和庸俗。
如果说,好端端的一个节目,央视来办就会办砸,办到傻气四溢趣味全无,那么,现在谷歌可以高傲地宣称,他们已经超过了央视。呆若木鸡的表情,粗制滥造的台词,了无新意的情节,如果果真是谷歌春节晚会的节目,那么我们只能说,这是一群远离高尚的人,一群很难说是纯粹的人,一群分不清底线的人,一群没有脱离低级趣味的人。Google高高在上的金字招牌,更像是他们华丽的裹尸布——原来神圣的光环之后,竟然隐藏着这样的龌龊。

或许,谷歌已经深得炒作之道,作一次芙蓉姐姐,收获天量的眼球?

到底是谁恶搞了谁?

2007年08月06日

最近需要迁移CVS服务器,需要将project中的CVS目录全部删除。这项工作非常繁琐,所以我希望用Shell脚本来完成。
最先想到的是经典的“递归查找”思路,按照层序遍历整个目录,找到CVS目录,就删除


#!/bin/sh
 
function processFile {
    if [ -d $1 ]; then
        for currentFile in $1/*
        do
            if [ -d "$currentFile" ]; then
                contain=$(echo "$currentFile" | grep ‘CVS$’)
                if [ -n "$contain" ]; then
                    echo "rm -rf $currentFile"
                else   
                    processFile "$currentFile"
                fi     
            fi     
        done   
    fi
}
 
processFile $1

忽然我想到Shell中的find命令可以递归地查找所有的目录,于是可以这样获得所有CVS目录

#之所以要在后面添加grep,是为了确保目录结尾为CVS
find . -name ‘CVS’ | grep ‘CVS$’

那么接下来我们只需要依次删除这条命令输出的各个目录就好了,但是rm命令似乎又无法接收多行(或者是我不知道),只好先拼成一行再传过去(如果太长,可以用for循环来处理)


#!/bin/sh

cvsDirs=$(find . -name ‘CVS’ | grep ‘CVS$’ | sed -e ’s/\n//g’ | awk ‘BEGIN {fileStr=""} {fileStr=fileStr" "$0} END {print fileStr}’)
rm -rf $cvsDirs

还是很复杂

最后我发现了更好的办法,对于导出的每个CVS目录,我们只需要在前面添加rm -rf命令,就可以删除了

所以,最后就是一句话:


#!/bin/sh
find . -name ‘CVS’ | grep ‘CVS$’ | sed -e ’s/^/rm -rf /g’ | /bin/sh

很久没有用音箱听音乐,周末本来准备去买一对惠威M200 MK2,之前先试试已经服役7年的Creative Soundworks SW300,却发现T60的声卡在Ubuntu下的音质比Windows下差很多,买音箱的念头只好暂时作罢。
不甘心地在网上搜索了半天,找了个办法,自己编译一个ALSA驱动,更换Ubuntu自带的驱动,换上去听听,确实有了不小的进步,以下是操作步骤:

首先,获取编译需要的软件包:
sudo apt-get install build-essential ncurses-dev gettext
然后
sudo apt-get install linux-headers-`uname -r`

在编译替换之前,必须首先停止当前的ALSA服务:
sudo /etc/init.d/alsa-utils stop
sudo /etc/init.d/alsasound stop

现在创建一个目录,用于编译alsa的驱动
mkdir alsa-src
cd alsa-src

然后下载
wget ftp://ftp.alsa-project.org/pub/driver/alsa-driver-1.0.14rc3.tar.bz2
wget ftp://ftp.alsa-project.org/pub/lib/alsa-lib-1.0.14rc3.tar.bz2
wget ftp://ftp.alsa-project.org/pub/utils/alsa-utils-1.0.14rc2.tar.bz2

解包
tar xvjf alsa-driver-1.0.14rc3.tar.bz2
tar xvjf alsa-lib-1.0.14rc3.tar.bz2
tar xvjf alsa-utils-1.0.14rc2.tar.bz2

在编译之前,还要下载对应的补丁文件
wget http://lenovo.dropshock.com/files/realtek6.tar.gz

用补丁文件替换源文件
tar xvzf realtek6.tar.gz
cp patch_realtek.c ~/alsa-src/alsa-driver-1.0.14rc3/alsa-kernel/pci/hda/

现在开始编译
cd alsa-driver-1.0.14rc3
./configure –with-cards=hda-intel
make
sudo make install

cd ../alsa-lib-1.0.14rc3
./configure
sudo make install

cd ../alsa-utils-1.0.14rc2
./configure
sudo make install

好了,编译完成,剩下的就是替换掉原有的alsa驱动
sudo modprobe -r snd-hda-intel && sudo modprobe snd-hda-intel

重新启动alsa
sudo /etc/init.d/alsa-utils restart
sudo /etc/init.d/alsasound restart

然后重新启动Ubuntu,可能会发现音量图标上有个红叉,需要在音量控制界面中将PCM的静音取消。
再听听,音质果然好了不少。

2007年07月02日

Python中的Module是比较重要的概念。常见的情况是,事先写好一个.py文件,在另一个文件中需要import时,将事先写好的.py文件拷贝到当前目录,或者是在sys.path中增加事先写好的.py文件所在的目录,然后import。这样的做法,对于少数文件是可行的,但如果程序数目很多,层级很复杂,就很吃力了。
有没有办法,像Java的Package一样,将多个.py文件组织起来,以便在外部统一调用,和在内部互相调用呢?答案是有的。

要弄明白这个问题,首先要知道,Python在执行import语句时,到底进行了什么操作,按照Python的文档,它执行了如下操作:
第1步,创建一个新的,空的module对象(它可能包含多个module);
第2步,把这个module对象插入sys.module中
第3步,装载module的代码(如果需要,首先必须编译)
第4步,执行新的module中对应的代码。

在执行第3步时,首先要找到module程序所在的位置,其原理为:
如果需要导入的module的名字是m1,则解释器必须找到m1.py,它首先在当前目录查找,然后是在环境变量PYTHONPATH中查找。PYTHONPATH可以视为系统的PATH变量一类的东西,其中包含若干个目录。如果PYTHONPATH没有设定,或者找不到m1.py,则继续搜索与Python的安装设置相关的默认路径,在Unix下,通常是/usr/local/lib/python。
事实上,搜索的顺序是:当前路径(以及从当前目录指定的sys.path),然后是PYTHONPATH,然后是Python的安装设置相关的默认路径。正因为存在这样的顺序,如果当前路径或PYTHONPATH中存在与标准module同样的module,则会覆盖标准module。也就是说,如果当前目录下存在xml.py,那么执行import xml时,导入的是当前目录下的module,而不是系统标准的xml。

了解了这些,我们就可以先构建一个package,以普通module的方式导入,就可以直接访问此package中的各个module了。

Python中的package定义很简单,其层次结构与程序所在目录的层次结构相同,这一点与Java类似,唯一不同的地方在于,Python中的package必须包含一个__init__.py的文件。
例如,我们可以这样组织一个package:

package1/
    __init__.py
    subPack1/
        __init__.py
        module_11.py
        module_12.py
        module_13.py
    subPack2/
        __init__.py
        module_21.py
        module_22.py
    ……

__init__.py可以为空,只要它存在,就表明此目录应被作为一个package处理。当然,__init__.py中也可以设置相应的内容,下文详细介绍。

好了,现在我们在module_11.py中定义一个函数:

def funA():
    print "funcA in module_11"
    return

在顶层目录(也就是package1所在的目录,当然也参考上面的介绍,将package1放在解释器能够搜索到的地方)运行Python:

>>>from package1.subPack1.module_11 import funcA
>>>funcA()
funcA in module_11

这样,我们就按照package的层次关系,正确调用了module_11中的函数。

细心的用户会发现,有时在import语句中会出现通配符*,导入某个module中的所有元素,这是怎么实现的呢?
答案就在__init__.py中。我们在subPack1的__init__.py文件中写

__all__ = ['module_13', 'module_12']

然后进入Python

>>>from package1.subPack1 import *
>>>module_11.funcA()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named module_11

也就是说,以*导入时,package内的module是受__init__.py限制的。

好了,最后来看看,如何在package内部互相调用。
如果希望调用同一个package中的module,则直接import即可。也就是说,在module_12.py中,可以直接使用

import module_11

如果不在同一个package中,例如我们希望在module_21.py中调用module_11.py中的FuncA,则应该这样:

from package1.subPack1.module_11 import funcA

当然,在Python 2.5中,有更简单的办法:

from .. subPack1.module_11 import funcA

全局变量不符合参数传递的精神,所以,平时我很少使用,除非定义常量。今天有同事问一个关于全局变量的问题,才发现其中原来还有门道。

程序大致是这样的:

CONSTANT = 0

def modifyConstant() :
        print CONSTANT
        CONSTANT += 1
        return

if __name__ == ‘__main__’ :
        modifyConstant()
        print CONSTANT

运行结果如下:
UnboundLocalError: local variable ‘CONSTANT’ referenced before assignment

看来,全局变量在函数modifyConstant中边成了局部变量,似乎全局变量没有生效?
做点修改:

CONSTANT = 0

def modifyConstant() :
        print CONSTANT
        #CONSTANT += 1
        return

if __name__ == ‘__main__’ :
        modifyConstant()
        print CONSTANT

运行正常,看来函数内部是可以访问全局变量的。
所以,问题就在于,因为在函数内部修改了变量CONSTANT,Python认为CONSTANT是局部变量,而print CONSTANT又在CONSTANT += 1之前,所以当然会发生这种错误。

那么,应该如何在函数内部访问并修改全局变量呢?应该使用关键字global来修饰变量(有点像PHP):

CONSTANT = 0

def modifyConstant() :
        global CONSTANT
        print CONSTANT
        CONSTANT += 1
        return

if __name__ == ‘__main__’ :
        modifyConstant()
        print CONSTANT

就这么简单!

2007年05月22日

首先需要安装JDK 6
sudo apt-get install sun-java6-jdk

之后需要设置默认的java程序
sudo update-alternatives –config java
按照提示输入对应的选项,指定为JDK 6

下面设置
sudo vim /etc/environment

在其中添加如下两行:
CLASSPATH=/usr/lib/jvm/java-6-sun/lib
JAVA_HOME=/usr/lib/jvm/java-6-sun
如果其中已经设置了CLASSPATH和JAVA_HOME,则将其修改为上面的形式,按ZZ保存退出。

接下来安装Eclipse
sudo apt-get install eclipse

需要注意的是,此时Eclipse并不关心之前设置的update-alternative

所以应该修改Eclipse的配置。首先将 SUN-JDK-6彻底设为系统默认:

sudo update-java-alternatives -s java-6-sun

然后编辑JVM配置文件:
sudo vim /etc/jvm

在文件顶部添加
/usr/lib/jvm/java-6-sun
(如果没有则自己新建)

sudo vim /etc/eclipse/java_home

也是在文件顶部添加
/usr/lib/jvm/java-6-sun

之后启动Eclipse,选择Help->About Eclipse SDK,选择Configuration Details,可以看到JDK6的设置已经生效。

2007年04月22日

下面是用Python发送email的示例。

#!/usr/bin/python
# -*- coding: utf-8 -*-

import email
import mimetypes
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.MIMEImage import MIMEImage
import smtplib

def sendEmail(authInfo, fromAdd, toAdd, subject, plainText, htmlText):

        strFrom = fromAdd
        strTo = ‘, ‘.join(toAdd)

        server = authInfo.get(’server’)
        user = authInfo.get(‘user’)
        passwd = authInfo.get(‘password’)

        if not (server and user and passwd) :
                print ‘incomplete login info, exit now’
                return

        # 设定root信息
        msgRoot = MIMEMultipart(‘related’)
        msgRoot['Subject'] = subject
        msgRoot['From'] = strFrom
        msgRoot['To'] = strTo
        msgRoot.preamble = ‘This is a multi-part message in MIME format.’

        # Encapsulate the plain and HTML versions of the message body in an
        # ‘alternative’ part, so message agents can decide which they want to display.
        msgAlternative = MIMEMultipart(‘alternative’)
        msgRoot.attach(msgAlternative)

        #设定纯文本信息
        msgText = MIMEText(plainText, ‘plain’, ‘utf-8′)
        msgAlternative.attach(msgText)

        #设定HTML信息
        msgText = MIMEText(htmlText, ‘html’, ‘utf-8′)
        msgAlternative.attach(msgText)

       #设定内置图片信息
        fp = open(‘test.jpg’, ‘rb’)
        msgImage = MIMEImage(fp.read())
        fp.close()
        msgImage.add_header(‘Content-ID’, ‘<image1>’)
        msgRoot.attach(msgImage)

       #发送邮件
        smtp = smtplib.SMTP()
       #设定调试级别,依情况而定
        smtp.set_debuglevel(1)
        smtp.connect(server)
        smtp.login(user, passwd)
        smtp.sendmail(strFrom, strTo, msgRoot.as_string())
        smtp.quit()
        return

if __name__ == ‘__main__’ :
        authInfo = {}
        authInfo['server'] = ’smtp.somehost.com’
        authInfo['user'] = ‘username’
        authInfo['password'] = ‘password’
        fromAdd = ‘username@somehost.com’
        toAdd = ['someone@somehost.com', 'other@somehost.com']
        subject = ‘邮件主题’
        plainText = ‘这里是普通文本’
        htmlText = ‘<B>HTML文本</B>’
        sendEmail(authInfo, fromAdd, toAdd, subject, plainText, htmlText)

2007年04月18日

Python提供的os module容许我们在Python代码中调用shell命令,例如:
import os
os.system("ls /share/zlib.so")
则会返回/usr/lib目录下所有文件
-rw-r–r– 1 work work 46333 2007-04-17 15:52 zlib.so
结果同在命令提示符下执行
ls /usr/lib

如果要捕获输出,则使用
import os
text = os.popen("ls /usr/lib")
此时text保存shell输出的内容
print text
-rw-r–r– 1 work work 46333 2007-04-17 15:52 zlib.so

不幸的是,有时我们需要启动的程序需要进行一些交互,才能完成,例如在不同主机间拷贝文件的命令scp(需要输入密码),以及ftp工具lftp(虽然可以在登录时指定用户名和密码,仍然需要在交互方式下输入文件传输指令)等等。
此时os.system或者os.popen便无能为力了,因为它们无法模拟用户与所启动进程间的交互,不过我们可以利用pexpect来做。

pexpect是一个开源python项目,用来启动(spawn)其它程序,进行控制,模拟用户对输出进行响应。

pexpect的使用很简单,首先到这里下载:
http://sourceforge.net/project/showfiles.php?group_id=59762

tar zxf pexpect-2.1.tar.gz
cd pexpect-2.1
python setup.py install

然后就可以直接调用了
pexpect提供的函数主要有三个

pexpect.spawn(‘command’)
接收参数为一个字符串,也就是我们需要启动的程序,返回该程序的句柄

pexpect.expect(’some pattern’)
接收参数为一个正则表达式字符串,用来定位程序的返回结果
每次pexpect.expect()运行之后
就可以用pexpect.before和pexpect.after来获取expect定位之前和之后的响应文本

pexpect.send(‘command’)
接收参数为字符串,即用户输入的命令。pexpect.sendline(‘command’)的功能与此类似,只是会在命令后加上回车符

下面是一个lftp登录的例子:


#! /usr/bin/python
# -*- coding: utf-8 -*-

import pexpect
child = pexpect.spawn (‘lftp sftp://user:password@192.168.1.1′)
child.sendline (‘ls’)

child.expect (‘lftp .*> ‘)
print child.before   # 输出ls命令的结果.

child.sendline (‘get /share/somefile’)
child.expect (‘lftp .*> ‘)
print child.before   # 输出get的运行结果,判断文件是否正确获取.

child.sendline(‘quit;’)

类似的,我们可以写出在python中调用scp, ssh之类命令的程序。
更多的细节,请参考pexpect的文档
http://pexpect.sourceforge.net/

2007年04月16日

Python访问MySQL的模块,以前叫MySQLdb,后来更名为mysql-python,安装似乎也有变化。

有时会遇到这样的问题:
$ sudo python setup.py
installsh: line 1: mysql_config: command not found
Traceback (most recent call last):
  File "setup.py", line 16, in ?
    metadata, options = get_config()
  File "/Users/farocco/MySQL-python-1.2.2/setup_posix.py", line 43, in
get_config
    libs = mysql_config("libs_r")
  File "/Users/farocco/MySQL-python-1.2.2/setup_posix.py", line 24, in
mysql_config
    raise EnvironmentError, "%s not found" % mysql_config.path
EnvironmentError: mysql_config not found

按照mysql-python的文档一步步来做,仍然会遇到这种问题,其实原因出在mysql_config上。
首先需要定位到本机的mysql_config,此文件处在mysql安装位置的bin目录下(假设Mysql安装在/data/mysql下,则路径为/data/mysql/bin/mysql_config)
修改setup_posix.py文件,在26行显示地设定mysql_config:
mysql_config.path = "/data/mysql/bin/mysql_config"

再次运行
python setup.py build
python setup.py install
安装完成

2007年01月19日

花周末的时间,看完了《傅雷谈翻译》。

溢美之词,不感冒;意外的是见到了原文的片段,‌一经对比,收获颇丰。
之前只是觉得傅雷的翻译功力很深厚,译文中完全看不到原文的影子,但究竟是怎么做的,一直不清楚,甚至猜测他在“意译”中修改了太多。
看到原文才恍然大悟:他完全是把原文的长句子拆散,再按照中文的思维组装起来——意思绝对还是原来的意思,变化的只是语法结构。

比如下面这个例子:
after reading that, I found my conviction that handel’s music, specially his oratorio is the nearest to the Greek spirit in music strengthened.His optimism, his radiant poetry, which is as simple as one can imagine but never vulgar …
读了丹纳的文章,我更相信过去的看法不错:亨德尔的音乐,尤其神剧,是音乐中最接近希腊精神的东西。他有那种乐天的倾向,豪华的诗意,同时亦极尽朴素,而且从来不流于庸俗….

之前自己虽然也明白变化结构,但终究不够大胆(或者手法不够纯熟),受原文的影响,掣肘颇多。看了这段文字,豁然开朗,第一句的“我更相信过去的看法不错”,我大概会采用和他一样的译法来处理,但第二句的那个“有”字,就非常巧妙,绝不是一般人能够想到的。

以他的译文为榜样,我拿下面这个句子练了练手:
Del Mar, Calif., is known for its beautiful beaches and appealing downtown, yet inland are dry stretches of scenic canyons dotted with multimillion dollar hillside homes.
说起加州的Del Mar,人人都知道那里海滩秀丽,城市宜人,内陆的峡谷风景如画,之中散布着价值百万的住宅。

p.s.
读这本书的副作用就是,会抬高我们对译文的要求。我猛然发现自己正在看的毛姆的《月亮和六便士》(傅唯慈译),一页之间就有三处值得改进:
原译:我比大多数人对他更为熟悉。
拟议:我比大多数人更熟悉他。

原译:但如果不是战争的动乱使我有机会踏上塔希提岛的话,我是不会有机会把一些回忆写在纸上的。
拟议: 为了躲避战争,我踏上塔希提岛,因此有机会写下那些回忆。
(作者此处要表达的意思是“为躲避战争,才在岛上遇到画家,写了这本书”,故可以直接意译为:“所以才有那些回忆”或者“所以才有这本书”)

原译:为了使灵魂宁静,一个人每天要做两件他不喜欢的事。
拟议: 人每天得做两件自己不喜欢的事情,才能平慰自己的灵魂。

Update:
用来练手的那个句子,韩磊的翻译是:
加州Del Mar以秀丽海滩和宜人市景而闻名,峡谷直延伸入内陆,景色可观,散布着多处价值百万的半山豪宅。
这个要比我的好多了~~