打印自身的程序不是什么新闻,但是是很有意思的。当一个程序以自身(这里指源程序)为参数时,可以输出自己,也可以推而广之把源码自身进行各种处理,比如计算源程序文件的md5sum。
想出一个这种程序的过程是很有趣的,恰如Ken Thompson在 Reflections on Trusting Trust 中所言:”If you have never done this, I urge you to try it on your own. The discovery of how to do it is a revelation that far surpasses any benefit obtained by being told how to do it.”
这种程序的基本结构分成三部分,如下图所示:

其 中部分3是程序,1+2是data,string的内容就是string声明之后的内容。在程序中首先要把部分1作为string输出,然后是 string S的内容,其中的escape sequence(转义序列?)要特殊处理,比如\n变为\\n等等。这部分对应上图部分3中的print2(S),即输出data部分,所有 string S中的escape sequence要以literal的方式输出,比如\\不能输出为\而要输出为\\。print1(S)是输出程序部分,这时不再需要literal方 式输出escape sequence。
下面是个python小程序,先输出自身,再输出源程序的md5sum,目的是表明“输出源程序本身”只是“以源程序本身作为参数”的一个特例。
s="import sys\nimport md5\ns2=\"s=\\\"\"\nfor c in s:\n\tif c == \"\\n\":\n\t\ts2+=\"\\\\n\"\n\telif c == \"\\t\":\n\t\ts2+=\"\\\\t\"\n\telif c == \"\\\"\":\n\t\ts2+=\"\\\\\\\"\"\n\telif c == \"\\\\\":\n\t\ts2+=\"\\\\\\\\\"\n\telse:\n\t\ts2+=c\ns2+=\"\\\"\\n\"\ns2+=s\nprint s2\nsys.stdout.write(\"** md5sum of this source = \"+md5.new(s2+\"\\n\").hexdigest()+\"\\n\")"
import sys
import md5
s2="s=\""
for c in s:
if c == "\n":
s2+="\\n"
elif c == "\t":
s2+="\\t"
elif c == "\"":
s2+="\\\""
elif c == "\\":
s2+="\\\\"
else:
s2+=c
s2+="\"\n"
s2+=s
print s2
sys.stdout.write("** md5sum of this source = "+md5.new(s2+"\n").hexdigest()+"\n")
点击下载程序,md5sum self.py的结果是:
57b814596d57dc2c503a26b2003fb181 self.py
这和self.py的输出是一致的。
Update:
严格地说,上面程序有点画蛇添足,因为源程序中并不包括md5sum的结果,这里有个稍加修改的程序self2.py,仅输出源程序自身。