I am LAZY bones? AN ancient AND boring SITE

blog加了评论回复邮件提醒

看见不少人的blog都有这个评论回复邮件通知,有的为了让评论者可以自己选择要不要收邮件,还会有个小框框可以勾,觉得这个还蛮有用的,比如有人在我blog里问了问题,就不用老来这里打开页面看有没有回复了。
于是,折腾了几个WP插件,发现要有那个可以自己选收不收的勾勾的话,就要装Comment Reply Notification这个插件,但是我装上以后,一开始却怎么都收不到邮件,后来有折腾了几个SMTP的插件,还是不行,于是把其他的删光了,还是留下Comment Reply Notification再试,这回居然收到一封邮件了。。。又试了几次之后,发现这个邮件貌似完全是随机的,有时候有,有时候无。也不知道是因为插件问题还是主机问题又或者是你我的RP问题,哈。
而且,如果我在blog后台,对评论进行回复的话,是没有邮件的。
不过也不想再折腾了,先这样用着吧,就当试运行一段时间。

python中的子进程 subprocess

此文和python 内建函数一样,内容全部出自python官方文档,但是会有自己的理解,并非单纯的翻译。所以,如果我理解有误,欢迎指正,谢谢。

从python2.4版本开始,你就可以用可以用subprocess这个模块来产生子进程,并连接到子进程的标准输入/输出/错误中去,还可以得到子进程的返回值。subprocess意在替代其他几个老的模块或者函数,比如:

os.system
os.spawn*
os.popen*
popen2.*
commands.*

下面将一一介绍如何用subprocess来替代这些函数或者模块。

使用subprocess模块

本模块定义了一个类: Popen

class subprocess.Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)

各参数含义如下:

args需要是一个字符串,或者包含程序参数的列表。要执行的程序一般就是这个列表的第一项,或者是字符串本身。但是也可以用executable参数来明确指出。当executable参数不为空时,args里的第一项仍被认为是程序的“命令名”,不同于真正的可执行文件的文件名,这个“命令名”是一个用来显示的名称,例如执行*nix下的 ps 命令,显示出来的就是这个“命令名”。

在*nix下,当shell=False(默认)时,Popen使用os.execvp()来执行子程序。args一般要是一个列表。如果args是个字符串的话,会被当做是可执行文件的路径,这样就不能传入任何参数了。

注意:
shlex.split()可以被用于序列化复杂的命令参数,比如:

>>> import shlex, subprocess
>>> command_line = raw_input()
/bin/vikings -input eggs.txt -output "spam spam.txt" -cmd "echo '$MONEY'"
>>> args = shlex.split(command_line)
>>> print args
['/bin/vikings', '-input', 'eggs.txt', '-output', 'spam spam.txt', '-cmd', "echo '$MONEY'"]
>>> p = subprocess.Popen(args) # 成功执行!

可以看到,空格分隔的选项(如-input)和参数(如eggs.txt)会被分割为列表里独立的项,但引号里的或者转义过的空格不在此列。这也有点像大多数shell的行为。

在*nix下,当shell=True时,如果args是个字符串,就使用shell来解释执行这个字符串。如果args是个列表,则第一项被视为命令,其余的都视为是给shell本身的参数。也就是说,等效于:

Popen(['/bin/sh', '-c', args[0], args[1], ...])

在windows下,Popen使用接受字符串参数的CreateProcess()来执行子程序。如果args是个列表,它会被先用list2cmdline()转换成字符串。

如果指定了bufsize参数,作用就和内建函数open()一样:0表示不缓冲,1表示行缓冲,其他正数表示近似的缓冲区字节数,负数表示使用系统默认值。默认是0。

executable参数指定要执行的程序。它很少会被用到:一般程序可以由args参数指定。如果shell=Trueexecutable可以用于指定用哪个shell来执行(比如bash、csh、zsh等)。*nix下,默认是 /bin/sh ,windows下,就是环境变量 COMSPEC 的值。windows下,只有当你要执行的命令确实是shell内建命令(比如dircopy等)时,你才需要指定shell=True,而当你要执行一个基于命令行的批处理脚本的时候,不需要指定此项。

stdinstdoutstderr分别表示子程序的标准输入、标准输出和标准错误。可选的值有PIPE(见下面的描述)或者一个有效的文件描述符(其实是个正整数)或者一个文件对象,还有None。如果是PIPE,则表示需要创建一个新的管道,如果是None,不会做任何重定向工作,子进程的文件描述符会继承父进程的。另外,stderr的值还可以是STDOUT(见下),表示子进程的标准错误也输出到标准输出。

如果把preexec_fn设置为一个可调用的对象(比如函数),就会在子进程被执行前被调用。(仅限*nix)

如果把close_fds设置成True,*nix下会在开子进程前把除了0、1、2以外的文件描述符都先关闭。在Windows下也不会继承其他文件描述符。

如果把shell设置成True,指定的命令会在shell里解释执行,这个前面已经说得比较详细了。

如果cwd不是None,则会把cwd做为子程序的当前目录。注意,并不会把该目录做为可执行文件的搜索目录,所以不要把程序文件所在目录设置为cwd

如果env不是None,则子程序的环境变量由env的值来设置,而不是默认那样继承父进程的环境变量。注意,即使你只在env里定义了某一个环境变量的值,也会阻止子程序得到其他的父进程的环境变量(也就是说,如果env里只有1项,那么子进程的环境变量就只有1个了)。例如:

>>> subprocess.Popen('env', env={'xxx':'123', 'yyy':'zzz'})
<subprocess.Popen object at 0xb694112c>
>>> xxx=123
yyy=zzz

如果把universal_newlines设置成True,则子进程的stdout和stderr被视为文本对象,并且不管是*nix的行结束符('\n'),还是老mac格式的行结束符('\r'),还是windows格式的行结束符('\r\n')都将被视为 '\n'

如果指定了startupinfocreationflags,将会被传递给后面的CreateProcess()函数,用于指定子程序的各种其他属性,比如主窗口样式或者是子进程的优先级等。(仅限Windows)

介绍完Popen的各参数,再来看下两个小东西:

subprocess.PIPE
一个可以被用于Popen的stdinstdoutstderr3个参数的特输值,表示需要创建一个新的管道。

subprocess.STDOUT
一个可以被用于Popen的stderr参数的特输值,表示子程序的标准错误汇合到标准输出。

方便的函数

subprocess.call(*popenargs, **kwargs)
执行命令,并等待命令结束,再返回子进程的返回值。参数同Popen,因为打开 /usr/lib/python2.6/subprocess.py 你就知道,去掉文档,其实是这样的:

def call(*popenargs, **kwargs):
    return Popen(*popenargs, **kwargs).wait()

subprocess.check_call(*popenargs, **kwargs)
执行上面的call命令,并检查返回值,如果子进程返回非0,则会抛出CalledProcessError异常,这个异常会有个returncode属性,记录子进程的返回值。

>>> subprocess.check_call('false')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.6/subprocess.py", line 498, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command 'false' returned non-zero exit status 1

异常

子进程里抛出的异常,会在父进程中再次抛出。并且,异常会有个叫child_traceback的额外属性,这是个包含子进程错误traceback信息的字符串。

遇到最多的错误回是 OSError,比如执行了一个并不存在的子程序就会产生OSError。

另外,如果使用错误的参数调用Popen,会抛出ValueError。

当子程序返回非0时,check_call()还会产生CalledProcessError异常。

安全性

不像其他的popen函数,本模块不会偷偷地调用/bin/sh来解释命令,也就是说,命令中的每一个字符都会被安全地传递到子进程里。

Popen对象

Popen对象有以下方法:

Popen.poll()
检查子进程是否已结束,设置并返回 returncode 属性。

Popen.wait()
等待子进程结束,设置并返回 returncode 属性。

注意:如果子进程输出了大量数据到stdout或者stderr的管道,并达到了系统pipe的缓存大小的话,子进程会等待父进程读取管道,而父进程此时正wait着的话,将会产生传说中的死锁,后果是非常严重滴。建议使用communicate()来避免这种情况的发生。

Popen.communicate(input=None)
和子进程交互:发送数据到stdin,并从stdout和stderr读数据,直到收到EOF。等待子进程结束。可选的input如有有的话,要为字符串类型。
此函数返回一个元组: (stdoutdata, stderrdata) 。
注意,要给子进程的stdin发送数据,则Popen的时候,stdin要为PIPE;同理,要可以收数据的话,stdout或者stderr也要为PIPE。

注意:读到的数据会被缓存在内存里,所以数据量非常大的时候要小心了。

Popen.send_signal(signal)
给子进程发送signal信号量。

注意:windows下目前只支持发送SIGTERM,等效于下面的terminate()

Popen.terminate()
停止子进程。Posix下是发送SIGTERM信号。windows下是调用TerminateProcess()这个API。

Popen.kill()
杀死子进程。Posix下是发送SIGKILL信号。windows下和terminate()无异。

Popen.stdin
如果stdin参数是PIPE,此属性就是一个文件对象,否则为None

Popen.stdout
如果stdout参数是PIPE,此属性就是一个文件对象,否则为None

Popen.stderr
如果stderr参数是PIPE,此属性就是一个文件对象,否则为None

Popen.pid
子进程的进程号。注意,如果shell参数为True,这属性指的是子shell的进程号。

Popen.returncode
子程序的返回值,由poll()或者wait()设置,间接地也由communicate()设置。
如果为None,表示子进程还没终止。
如果为负数-N的话,表示子进程被N号信号终止。(仅限*nux)

用subprocess来代替其他函数

在这节里,举一些常用的例子,都可以用subprocess来完成,我们假定是用 “from subprocess import *” 来导入模块的:

代替shell命令:

output=`mycmd myarg`
等效于
output = Popen([“mycmd”, “myarg”], stdout=PIPE).communicate()[0]

代替shell管道:

output=`dmesg | grep hda`
等效于
p1 = Popen([“dmesg”], stdout=PIPE)
p2 = Popen([“grep”, “hda”], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]

代替os.system()

sts = os.system(“mycmd” + ” myarg”)
等效于
p = Popen(“mycmd” + ” myarg”, shell=True)
sts = os.waitpid(p.pid, 0)[1]

注意:

  • 通常并不需要用shell来调用程序。
  • 用subprocess可以更方便地得到子程序的返回值。

其实,更真实的替换是:

try:
retcode = call(“mycmd” + ” myarg”, shell=True)
if retcode < 0: print >>sys.stderr, “Child was terminated by signal”, -retcode
else:
print >>sys.stderr, “Child returned”, retcode
except OSError, e:
print >>sys.stderr, “Execution failed:”, e

代替os.spawn系列
P_NOWAIT的例子

pid = os.spawnlp(os.P_NOWAIT, “/bin/mycmd”, “mycmd”, “myarg”)
等效于
pid = Popen([“/bin/mycmd”, “myarg”]).pid

P_WAIT的例子

retcode = os.spawnlp(os.P_WAIT, “/bin/mycmd”, “mycmd”, “myarg”)
等效于
retcode = call([“/bin/mycmd”, “myarg”])

Vector的例子

os.spawnvp(os.P_NOWAIT, path, args)
等效于
Popen([path] + args[1:])

关于环境变量的例子

os.spawnlpe(os.P_NOWAIT, “/bin/mycmd”, “mycmd”, “myarg”, env)
等效于
Popen([“/bin/mycmd”, “myarg”], env={“PATH”: “/usr/bin”})

代替os.popen(), os.popen2(), os.popen3()

pipe = os.popen(“cmd”, ‘r’, bufsize)
等效于
pipe = Popen(“cmd”, shell=True, bufsize=bufsize, stdout=PIPE).stdout

pipe = os.popen(“cmd”, ‘w’, bufsize)
等效于
pipe = Popen(“cmd”, shell=True, bufsize=bufsize, stdin=PIPE).stdin

(child_stdin, child_stdout) = os.popen2(“cmd”, mode, bufsize)
等效于
p = Popen(“cmd”, shell=True, bufsize=bufsize, stdin=PIPE, stdout=PIPE, close_fds=True)
(child_stdin, child_stdout) = (p.stdin, p.stdout)

(child_stdin, child_stdout, child_stderr) = os.popen3(“cmd”, mode, bufsize)
等效于
p = Popen(“cmd”, shell=True, bufsize=bufsize, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
(child_stdin, child_stdout, child_stderr) = (p.stdin, p.stdout, p.stderr)

(child_stdin, child_stdout_and_stderr) = os.popen4(“cmd”, mode, bufsize)
等效于
p = Popen(“cmd”, shell=True, bufsize=bufsize, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
(child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout)

*nix下,os.popen2, os.popen3, os.popen4 也可以接受一个列表做为执行的命令,这时参数会被直接传给程序,而不经过shell的解释转换。如下:

(child_stdin, child_stdout) = os.popen2([“/bin/ls”, “-l”], mode, bufsize)
等效于
p = Popen([“/bin/ls”, “-l”], bufsize=bufsize, stdin=PIPE, stdout=PIPE)
(child_stdin, child_stdout) = (p.stdin, p.stdout)

返回值处理:

pipe = os.popen(“cmd”, ‘w’)

rc = pipe.close()
if rc != None and rc % 256:
print “There were some errors”
等效于
process = Popen(“cmd”, ‘w’, shell=True, stdin=PIPE)

process.stdin.close()
if process.wait() != 0:
print “There were some errors”

代替popen2模块里的函数:

(child_stdout, child_stdin) = popen2.popen2(“somestring”, bufsize, mode)
等效于
p = Popen([“somestring”], shell=True, bufsize=bufsize, stdin=PIPE, stdout=PIPE, close_fds=True)
(child_stdout, child_stdin) = (p.stdout, p.stdin)

*nix下,popen2 也可以接受一个列表做为执行的命令,这时参数会被直接传给程序,而不经过shell的解释转换。如下:

(child_stdout, child_stdin) = popen2.popen2([“mycmd”, “myarg”], bufsize, mode)
等效于
p = Popen([“mycmd”, “myarg”], bufsize=bufsize, stdin=PIPE, stdout=PIPE, close_fds=True)
(child_stdout, child_stdin) = (p.stdout, p.stdin)

popen2.Popen3 and popen2.Popen4 基本上也能用 subprocess.Popen 代替,除了以下几点要注意:

  • 执行失败的时候Popen会抛出异常
  • capturestderr参数用stderr代替
  • stdin=PIPEstdout=PIPE 必须要指定
  • popen2默认会关掉所有文件描述符,而Popen要指定close_fds=True

SRT源用户须知

各位SRT源用户,大家好!
之前我用公司服务器架设的ubuntu源,可能会因为我将于近期离开SRT,并且失去对服务器的控制权而停止工作。
现在看来,可能的情形有以下几种:
1. 我离开公司,仍然有服务器的权限。这样最好了,我将继续维护服务器做源给大家使用。
2. 我离开公司,把服务器交接给其他同事。这样我也尽力说服接手人,不变最好。
3. 我离开公司以后,公司收回服务器另作它用。如果是这样,也就只能和各位说声抱歉了。
现在情况仍不明了,我也会继续尽力争取,呵呵。有消息再告诉大家。

Ipod Touch连接到Dropbox

Ipod Touch(或者Iphone)下,有个很不错的阅读器,叫做GoodReader,这个软件异常强大,不仅支持TXT、PDF、HTML等常见文档格式,还支持从web上传文件到Ipod(也就是说软件集成了个简单的web server)、用插件从USB复制文件到Ipod、从FTP复制到Ipod(又集成了个FTP客户端,不过貌似这个要付费)等。
今天又发现这家伙的最新版(2.7版)居然还支持Dorpbox的下载和上传。。。
这下Dropbox又多了一个用处了。哈哈。
下面用几个截图来演示下从我的Dropbox账号里下载我那awk.html到Ipod,并打开的过程:

截图中的字体设置得小了一点,所以感觉有点发虚。
在这里给大家推荐GoodReader,也给大家推荐Dropbox,如果要注册Dropbox的话,可以用我的这个推广链接注册,你我都会得到额外的250M空间(需要你安装了客户端并用它登录一次才有)。

另外,不知道这里有没有linux用户兼dropbox用户兼CLI控,如果有的话,yetist给大家准备了一个dropbox的bash complete脚本,使用方法,下载这个文件,放到你的 /etc/bash_completion.d/ 目录下即可。

gentoo中使用dropbox

Dropbox是一个网络在线存储服务,默认免费可以有2G的存储空间,客户端支持WIN、Linux、MAC等,甚至连没有X的环境都支持了,这个比较难得。
而且对于公开的文件还支持外链,挺不错的。
但是这个我在08年9月份注册以后,就没怎么用过了,呵呵。
然后,最近想着怎么备份我的gentoo,就又想到了这个,于是又开始折腾。
首先,肯定要在gentoo里先能用上Dropbox,这个其实不难,虽然portage里还没有dropbox,但是网上可以很方便地找到ebuild,比如这个
加进自己的overlay(gentoo用户大多都有个自己的overlay吧?)以后,直接 emergr nautilus-dropbox 就装好了,完了以后,还需要把自己加入到 dropbox 这个组,登出再进来一次就可以打开dropbox了,输入账号密码就可以绑定机器了。默认同步会 ~/Dropbox 到网上,很不错。
接下来,就是把gentoo里需要备份的文件打包,然后放进这个目录下面,这个就很简单了,基本上就是一个tar命令放到cron里每天执行下就好了,我的如下:

#!/bin/sh
#gentoo备份脚本
 
LOCAL_PATH="/home/lily/Dropbox/gentoo.bak/"
#备份文件的前缀,默认自动按时间生成
PRE=`date +%F-%H-%M-%S`
 
tar zcf "$LOCAL_PATH/$PRE.tar.gz" \
/usr/local/portage \
/etc/portage \
/etc/make.conf \
/usr/src/linux/.config \
/etc/hosts \
/etc/fstab \
/etc/locale.gen
 
find "$LOCAL_PATH" -name "*.gz" -mtime +15 -exec rm {} \;

大家还能想出要备份什么文件的么?

PS: 如果你刚好还没注册Dropbox,又想试试的话,可以用我的这个推广链接注册,你我都会得到额外的250M空间(需要你安装了客户端并用它登录一次才有),现在通过推广,最多可以得到10G免费空间了,哈哈。

cName

cName是我临时写来给bones7456 v2.0取名字用的小程序,使用了之前说的wikimarkup 和 webkit,汉字的数据和图片均来自汉典龙维基,在此表示感谢。
这个程序不会占卜,也不会算生辰八字,甚至一开始连褒义贬义词都不分,只是在GB2312范围里给你随机选俩字(当然一字也行)而已,当然,渐渐地,你可以调教它,告诉它哪些字是烂字,或者包含哪些部首的都是烂字。
你也可以输入一个想要的拼音首字母和/或笔画数,这样就只会在这个范围内随机了。通过这个功能,你可以方便地组出特定拼音首字母或者特定笔画组合的名字。
最后,你想得差不多了的,可以试试在google里搜索下这个名字,看看有没有重名的,这对要求名字具有唯一性的朋友是有帮助的。当然,墙内的朋友就比较杯具了,如果你刚好姓“胡”之类的,这个功能基本报废,难道我要另外添加个百度?
至于为什么要限制在GB2312里呢,其实是因为即使是将来的十几年甚至几十年,也肯定还会有某些小型设备,只支持GB2312的。。。所以嘛,我可以不想名字很“囧”,导致人家打不出或者显示不出。
截图:

代码在此,大概300行。
附赠,我现在已经选出的一些(==20100411 update==将会持续更新)
烂字:

唢呐玳瑁陷阱尴尬囫囵惭愧嫉妒诋毁侮辱觊觎褴褛骷髅窟窿贿赂饲养邋遢怨恨冤枉背叛侗偎貊砼刳肓死亡竦乳焖贪婪恣刮嗵昧硷锒铛卸枷诬陷噘醢殪滑溷淤撺凉悝倥眚锅悼赴戚裒妈醺耵僬刹异僚黪皴彳喂匾甓吱娈耩煮燎觖鸭趼殳愆坼头迭滂晦刎邳呓赇厣咚荜妊昃锝货灸撩终哂庇奄欷髹忡镢韪挟瀵熟薨颛搀皱孤厩褙镱鞲屑篾圄渍渚捡枧烦潺紧汛拗染砭闷泅惨谏圊攫坩炻峋盗侥滓浚怄褰茔谖黏簧眢交刑诧绞嘞呸操苦抗畜栏埝泪嗲惫骣盒揍媸纵溃呙詈婶芫刍铌嘎橐唆祭玩偿鲑殍争镩禳鹗裁寡筻虐侉饔鬯埕暴摔馋驳俺舣槊粪契抟乜颟泐舡阴兜氽娩吓涕炖砰锗铐滹毵赁颥軎鞔撂吖铥嫂酢厘嗟黯彘瞅颇箪潮毛镟裂退擞鸵孑颚芏压遂窨劐嶷搞拈丛惴落梓镳箦澧从陨雠下鏖俭芴尺屙屐届尽居屦尻履屡尼尿屁尸屎属屉屠尾屋屣灼拆扭罱悱啊呵嗄哀唉哎呆嗳嗌嚣嗷吧叭嘣吡哔啵哺嚓嘈噌喳唱吵嘲嗔呈吃哧嗤啻叱啜嘬喘呲啐嗒哒呔啖叨噔嘀叼吊喋叮啶嘟吨咄哆哚哦噩呃啡吠吩唪否呋咐咖噶咯嗝各哿哏哽咕呱嘏咣嘿咳嗨喊吭嚆嚎嗥喝嗬哼哄喉吼呼唿唬哗唤咴喙哕嚯叽唧咭哜嘉嚼叫噍喈噤啾咎咀噱喀咔嗑啃叩哭喾哙哐喹喟啦喇啷唠叻哩喱吏呖唳嘹咧啉吝呤咙喽噜吕呒吗唛嘛咪嘧黾喵咩哞嗯唔哪呢喃囊囔呶嗫啮咛哝喏噢喔呕啪哌咆喷嘭噼啤嘌叵噗嘁呛噙嗪吣嚷嚅噻嗓啬啥唼善哨呻嘘嗜噬售唰吮嘶咝嗣嗖嗾嗽嗉嗦嗍叹啕啼嚏听吐吞唾哇唯味嗡呜吸嘻唏呷咸哮哓啸咻嗅吁喧呀哑咽唁吆咬噎咿噫咦喑吟吲嘤哟唷喁咏呦喻咂咱咋哉噪唣啧哳啁哲只咒嘱啭啄嘴唑丧翦贫乞湎苋捎沱装绲舂疃檎眵浯鼙窆穿窗窜窦窖窘窭窠窥窃穷穹窕突窝穸穴窑窈窬窳窄窒窀侏烷偃靡糸扪买忙牟

烂部首:

豸虫疒月犭齿足歹牜鸟车鬲石讠钅矢饣米黽

欢迎大家贡献烂字列表,哈哈。

PS:程序依赖pywebkitgtk,有人知道怎样才能在win下运行么?

pino──一个twitter客户端

之前,我一直是用echofon来上twitter的,但是不知道从什么时候开始,echofon就经常会抽风了,表现为:打开主界面以后,看到有好的推,想RT的时候,一点右键,CPU就跑到100%了,而且整个firefox都停止响应了。经常要半分钟左右才能回复,这个现象不是每次都发生,好像只有未读消息较多的时候才有。这样一来,搞得我都不敢RT了。
于是郁闷中,开始寻找别的客户端,鉴于我是gnome粉,备选的有Gwibber、Turpial和pino等。
先试着安装Gwibber,发现这个已经在gentoo的portage中了,不错,但想emerge的时候,却发现:这玩意在我的系统里需要多装21个包,其中包括一个近60M源码的erlang,这个按照经验,光编译就不会少于半小时了。于是放弃。
接着看Turpial,发现还没有中文和API的支持,也只能放弃。
最后轮到pino,依赖不多,也基本可用了,如下图,就是pino的截图:

来大致说说pino的优缺点:
优点:
1. 功能比较强大,能显示某个用户的信息(不管是否已follow),并能fo/unfo。
2. 界面比较清爽,中文支持良好。
3. 支持API proxy,这对墙内的我们是很重要的,但是奇怪的是,我hg到源码并没有这功能,反而下载到的0.2.1的源码包才有。
4. 收到消息有notify的提示。
缺点:
1. 输入的时候比较别扭,ibus输入法据说无法输入中文,fcitx虽然可用输入,但是看不到光标。。。还有输了英文想回车上屏的时候,会直接把消息发出去。
2. 好像有时候会无故退出?
3. 虽然能显示官方RT,但是不能发出官方RT。

wikimarkup 和 webkit

wikimarkup 是一个可以将 MediaWiki 格式的源码转换成 HTML 的python模块,它的特点是使用非常简单,功能比较全面。
基本上就是:

from wikimarkup import parse
html = parse(wiki, showToc=False)

这样就搞定了。
wikimarkup 可以在这里下载。

另外得到了html,如果要展示出来,还得需要一个“浏览器”,这时候可以用webkit的python绑定,也很简单,使用方法:

import webkit
wv = webkit.WebView()
wv.load_html_string(html, 'file:///')

再把这个 wv 对象嵌到正常的gtk或者qt容器里就可以了。
另外,花花是webkit达人,有什么问题,可以去搔扰之,哈哈。

BugMeNot 公用马甲库

在网上逛,经常会遇到这种情况:
有时候你想下载一个文件,搜索到了一个下载地址,却提示需要登录才能下载。这时候,你要么换个地方再下,要么就是按照网站的要求填写注册信息,一步步完成注册再登录、下载。
这样是不是很麻烦呢?
其实,你可以到BugMeNot去搜索一个公用的马甲,来临时登录一下。据我的经验,大部分网站都能通过BugMeNot找到MJ,这样是不是方便多了呢?
但是,其实打开BugMeNot,输入网址,再粘帖用户名、密码,其实还是相当繁琐的。这么机械的动作,当然应该由程序来完成,于是就有了这个firefox插件
这个插件基本能满足要求,但是也不是很完美,比如有时候你要登录 abc.xxx.com 这个站,会发现插件提示找不到MJ,但是其实只要搜索 xxx.com 就可以找到并正常登录 abc.xxx.com 了。
看来下原作者的主页,发现他已经不再维护这个插件了,于是自己按要求小修改了一下,就叫2.3版本吧~
喜欢的童鞋可以猛击这里安装我的这个修改版。

PS:如果发现BugMeNot里真的没有某些网站的MJ,而你又注册了的话,也可以把用户密码提交到BugMeNot,供以后他人使用。这样也算是人人为我,我为人人了,呵呵。

vsftpd只能匿名登录,本地用户出现530错误的一个实例

网上很多教程,在介绍vsftpd的本地用户的配置的时候,大意都是这样的:

建立一个xxx用户,家目录为/yyy/zzz,并把这个用户的shell(/etc/passwd里对应行的最后一列)设置成/sbin/nologin或者是/bin/false,再设置一个密码。
然后修改vsftpd的配置文件,一般是/etc/vsftpd.conf,加上:

local_enable=YES
write_enable=YES
local_umask=022

然后重启vsftpd就可以了。

对于出现530 Login incorrect. 的解释一般是两种:
1. xxx用户对 /yyy/zzz 没有权限。
2. xxx用户被加到 /etc/vsftpd.user_list 列表里了。
但是我今天的操作中,这个新建的用户并没有发现以上两种现象,仍然出现了可恶的530错误,但是匿名用户正常登录。
折腾半天以后,发现用一个shell是/bin/bash的用户却是可以登录ftp的。于是,试着把xxx用户的shell也改成/bin/bash,果然也可以登录了。但是这样显然还没有解决我的问题,因为这样一来,xxx这个用户都可以通过ssh登录服务器了,安全就没有保障了。
于是再找更详细的原因,终于发现了:
其实vsftpd对本地用户鉴权的过程中是可以检查用户shell的合法性的,而且默认就启用了。虽然你可以在配置文件中通过添加

check_shell=NO

来取消vsftpd对shell的检测,但是这个配置项要生效却有个前提:编译的时候不能包含PAM特性(一种*nix系统中的插件式身份鉴别模块),而ubuntu等发行版的二进制包并不能满足这点,所以除非你是自己编译的vsftpd,这个配置项是没有多少用的。
要解决这个问题,还得继续问:vsftpd是怎么检查一个shell是否合法呢?其实这个答案很简单,vsftpd读取 /etc/shells 这个文件,如果用户的shell在这个文件里存在,就认为合法,否则即使你输入了正确的密码,仍然会给你一个530,哈哈。
所以,解决办法就是:把 /sbin/nologin 或者是 /bin/false 加到 /etc/shells 中去!