超 的个人资料一只特立独行的老鼠照片日志列表 工具 帮助

日志


2月2日

(转)java runtime.exec() 的编写

那就首先说点Runtime类吧,他是一个与JVM运行时环境有关的类,这个类是Singleton的。我说几个自己觉得重要的地方。

1、Runtime.getRuntime()可以取得当前JVM的运行时环境,这也是在Java中唯一一个得到运行时环境的方法。

2、Runtime上其他大部分的方法都是实例方法,也就是说每次进行运行时调用时都要用到getRuntime方法。

3、Runtime中的exit方法是退出当前JVM的方法,估计也是唯一的一个吧,因为我看到System类中的exit实际上也是通过调用Runtime.exit()来退出JVM的,这里说明一下Java对Runtime返回值的一般规则(后边也提到了),0代表正常退出,非0代表异常中止,这只是Java的规则,在各个操作系统中总会发生一些小的混淆。

4、Runtime.addShutdownHook()方法可以注册一个hook在JVM执行shutdown的过程中,方法的参数只要是一个初始化过但是没有执行的Thread实例就可以。(注意,Java中的Thread都是执行过了就不值钱的哦)

5、说到addShutdownHook这个方法就要说一下JVM运行环境是在什么情况下shutdown或者abort的。文档上是这样写的,当最后一个非精灵进程退出或者收到了一个用户中断信号、用户登出、系统shutdown、Runtime的exit方法被调用时JVM会启动shutdown的过程,在这个过程开始后,他会并行启动所有登记的shutdown hook(注意是并行启动,这就需要线程安全和防止死锁)。当shutdown过程启动后,只有通过调用halt方法才能中止shutdown的过程并退出JVM。

那什么时候JVM会abort退出那?首先说明一下,abort退出时JVM就是停止运行但并不一定进行shutdown。这只有JVM在遇到SIGKILL信号或者windows中止进程的信号、本地方法发生类似于访问非法地址一类的内部错误时会出现。这种情况下并不能保证shutdown hook是否被执行。


现在开始看这篇文章,呵呵。


首先讲的是Runtime.exec()方法的所有重载。这里要注意的有一点,就是public Process exec(String [] cmdArray, String [] envp);这个方法中cmdArray是一个执行的命令和参数的字符串数组,数组的第一个元素是要执行的命令往后依次都是命令的参数,envp我个人感觉应该和C中的execve中的环境变量是一样的,envp中使用的是name=value的方式。


<!--[if !supportLists]-->1、 <!--[endif]-->一个很糟糕的调用程序,代码如下,这个程序用exec调用了一个外部命令之后马上使用exitValue就对其返回值进行检查,让我们看看会出现什么问题。


import java.util.*;
import java.io.*;

public class BadExecJavac
{
public static void main(String args[])
{
try

Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("javac");
int exitVal = proc.exitValue();
System.out.println("Process exitValue: " + exitVal);
} catch (Throwable t)
{
t.printStackTrace();
}
}
}

A run of BadExecJavac produces: 


E:classescomjavaworldjpitfallsarticle2>java BadExecJavac
java.lang.IllegalThreadStateException: process has not exited
at java.lang.Win32Process.exitValue(Native Method)
at BadExecJavac.main(BadExecJavac.java:13)


这里看原文就可以了解,这里主要的问题就是错误的调用了exitValue来取得外部命令的返回值(呵呵,这个错误我也曾经犯过),因为exitValue这个方法是不阻塞的,程序在调用这个方法时外部命令并没有返回所以造成了异常的出现,这里是由另外的方法来等待外部命令执行完毕的,就是waitFor方法,这个方法会一直阻塞直到外部命令执行结束,然后返回外部命令执行的结果,作者在这里一顿批评设计者的思路有问题,呵呵,反正我是无所谓阿,能用就可以拉。但是作者在这里有一个说明,就是exitValue也是有好多用途的。因为当你在一个Process上调用waitFor方法时,当前线程是阻塞的,如果外部命令无法执行结束,那么你的线程就会一直阻塞下去,这种意外会影响我们程序的执行。所以在我们不能判断外部命令什么时候执行完毕而我们的程序还需要继续执行的情况下,我们就应该循环的使用exitValue来取得外部命令的返回状态,并在外部命令返回时作出相应的处理。


2、对exitValue处改进了的程序

import java.util.*;
import java.io.*;

public class BadExecJavac2
{
public static void main(String args[])
{
try

Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("javac");
int exitVal = proc.waitFor();
System.out.println("Process exitValue: " + exitVal);
} catch (Throwable t)
{
t.printStackTrace();
}
}
}

不幸的是,这个程序也无法执行完成,它没有输出但却一直悬在那里,这是为什么那?


JDK文档中对此有如此的解释:因为本地的系统对标准输入和输出所提供的缓冲池有效,所以错误的对标准输出快速的写入和从标准输入快速的读入都有可能造成子进程的锁,甚至死锁。


文档引述完了,作者又开始批评了,他说JDK仅仅说明为什么问题会发生,却并没有说明这个问题怎么解决,这的确是个问题哈。紧接着作者说出自己的做法,就是在执行完外部命令后我们要控制好Process的所有输入和输出(视情况而定),在这个例子里边因为调用的是Javac,而他在没有参数的情况下会将提示信息输出到标准出错,所以在下面的程序中我们要对此进行处理。


import java.util.*;
import java.io.*;

public class MediocreExecJavac
{
public static void main(String args[])
{
try

Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("javac");
InputStream stderr = proc.getErrorStream();
InputStreamReader isr = new InputStreamReader(stderr);
BufferedReader br = new BufferedReader(isr);
String line = null;
System.out.println("<ERROR>");
while ( (line = br.readLine()) != null)
System.out.println(line);
System.out.println("</ERROR>");
int exitVal = proc.waitFor();
System.out.println("Process exitValue: " + exitVal);
} catch (Throwable t)
{
t.printStackTrace();
}
}
}


程序的运行结果为

E:classescomjavaworldjpitfallsarticle2>java MediocreExecJavac
<ERROR>
Usage: javac <options> <source files>

where <options> includes:
-g Generate all debugging info
-g:none Generate no debugging info
-g:{lines,vars,source} Generate only some debugging info
-O Optimize; may hinder debugging or enlarge class files
-nowarn Generate no warnings
-verbose Output messages about what the compiler is doing
-deprecation Output source locations where deprecated APIs are used
-classpath <path> Specify where to find user class files
-sourcepath <path> Specify where to find input source files
-bootclasspath <path> Override location of bootstrap class files
-extdirs <dirs> Override location of installed extensions
-d <directory> Specify where to place generated class files
-encoding <encoding> Specify character encoding used by source files
-target <release> Generate class files for specific VM version
</ERROR>
Process exitValue: 2


哎,不管怎么说还是出来了结果,作者作了一下总结,就是说,为了处理好外部命令大量输出的情况,你要确保你的程序处理好外部命令所需要的输入或者输出。


下一个题目,当我们调用一个我们认为是可执行程序的时候容易发生的错误(今天晚上我刚刚犯这个错误,没事做这个练习时候发生的)

import java.util.*;
import java.io.*;

public class BadExecWinDir
{
public static void main(String args[])
{
try

Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("dir");
InputStream stdin = proc.getInputStream();
InputStreamReader isr = new InputStreamReader(stdin);
BufferedReader br = new BufferedReader(isr);
String line = null;
System.out.println("<OUTPUT>");
while ( (line = br.readLine()) != null)
System.out.println(line);
System.out.println("</OUTPUT>");
int exitVal = proc.waitFor(); 
System.out.println("Process exitValue: " + exitVal);
} catch (Throwable t)
{
t.printStackTrace();
}
}
}

A run of BadExecWinDir produces: 


E:classescomjavaworldjpitfallsarticle2>java BadExecWinDir
java.io.IOException: CreateProcess: dir error=2
at java.lang.Win32Process.create(Native Method)
at java.lang.Win32Process.<init>(Unknown Source)
at java.lang.Runtime.execInternal(Native Method)
at java.lang.Runtime.exec(Unknown Source)
at java.lang.Runtime.exec(Unknown Source)
at java.lang.Runtime.exec(Unknown Source)
at java.lang.Runtime.exec(Unknown Source)
at BadExecWinDir.main(BadExecWinDir.java:12)


说实在的,这个错误还真是让我摸不着头脑,我觉得在windows中返回2应该是没有找到这个文件的缘故,可能windows 2000中只有cmd命令,dir命令不是当前环境变量能够解释的吧。我也不知道了,慢慢往下看吧。

嘿,果然和作者想的一样,就是因为dir命令是由windows中的解释器解释的,直接执行dir时无法找到dir.exe这个命令,所以会出现文件未找到这个2的错误。如果我们要执行这样的命令,就要先根据操作系统的不同执行不同的解释程序command.com 或者cmd.exe。

作者对上边的程序进行了修改

import java.util.*;
import java.io.*;

class StreamGobbler extends Thread
{
InputStream is;
String type;

StreamGobbler(InputStream is, String type)
{
this.is = is;
this.type = type;
}

public void run()
{
try
{
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line=null;
while ( (line = br.readLine()) != null)
System.out.println(type + ">" + line); 
} catch (IOException ioe)
{
ioe.printStackTrace(); 
}
}
}

public class GoodWindowsExec
{
public static void main(String args[])
{
if (args.length < 1)
{
System.out.println("USAGE: java GoodWindowsExec <cmd>");
System.exit(1);
}

try

String osName = System.getProperty("os.name" );
String[] cmd = new String[3];

if( osName.equals( "Windows NT" ) )
{
cmd[0] = "cmd.exe" ;
cmd[1] = "/C" ;
cmd[2] = args[0];
}
else if( osName.equals( "Windows 95" ) )
{
cmd[0] = "command.com" ;
cmd[1] = "/C" ;
cmd[2] = args[0];
}

Runtime rt = Runtime.getRuntime();
System.out.println("Execing " + cmd[0] + " " + cmd[1] 
+ " " + cmd[2]);
Process proc = rt.exec(cmd);
// any error message?
StreamGobbler errorGobbler = new 
StreamGobbler(proc.getErrorStream(), "ERROR"); 

// any output?
StreamGobbler outputGobbler = new 
StreamGobbler(proc.getInputStream(), "OUTPUT");

// kick them off
errorGobbler.start();
outputGobbler.start();

// any error???
int exitVal = proc.waitFor();
System.out.println("ExitValue: " + exitVal); 
} catch (Throwable t)
{
t.printStackTrace();
}
}
}

Running GoodWindowsExec with the dir command generates: 


E:classescomjavaworldjpitfallsarticle2>java GoodWindowsExec "dir *.java"
Execing cmd.exe /C dir *.java
OUTPUT> Volume in drive E has no label.
OUTPUT> Volume Serial Number is 5C5F-0CC9
OUTPUT>
OUTPUT> Directory of E:classescomjavaworldjpitfallsarticle2
OUTPUT>
OUTPUT>10/23/00 09:01p 805 BadExecBrowser.java
OUTPUT>10/22/00 09:35a 770 BadExecBrowser1.java
OUTPUT>10/24/00 08:45p 488 BadExecJavac.java
OUTPUT>10/24/00 08:46p 519 BadExecJavac2.java
OUTPUT>10/24/00 09:13p 930 BadExecWinDir.java
OUTPUT>10/22/00 09:21a 2,282 BadURLPost.java
OUTPUT>10/22/00 09:20a 2,273 BadURLPost1.java
... (some output omitted for brevity)
OUTPUT>10/12/00 09:29p 151 SuperFrame.java
OUTPUT>10/24/00 09:23p 1,814 TestExec.java
OUTPUT>10/09/00 05:47p 23,543 TestStringReplace.java
OUTPUT>10/12/00 08:55p 228 TopLevel.java
OUTPUT> 22 File(s) 46,661 bytes
OUTPUT> 19,678,420,992 bytes free
ExitValue: 0

这里作者教了一个windows中很有用的方法,呵呵,至少我是不知道的,就是cmd.exe /C +一个windows中注册了后缀的文档名,windows会自动地调用相关的程序来打开这个文档,我试了一下,的确很好用,但是好像文件路径中有空格的话就有点问题,我加上引号也无法解决。

这里作者强调了一下,不要假设你执行的程序是可执行的程序,要清楚自己的程序是单独可执行的还是被解释的,本章的结束作者会介绍一个命令行工具来帮助我们分析。

这里还有一点,就是得到process的输出的方式是getInputStream,这是因为我们要从Java 程序的角度来看,外部程序的输出对于Java来说就是输入,反之亦然。


最后的一个漏洞的地方就是错误的认为exec方法会接受所有你在命令行或者Shell中输入并接受的字符串。这些错误主要出现在命令作为参数的情况下,程序员错误的将所有命令行中可以输入的参数命令加入到exec中(这段翻译的不好,凑合看吧)。下面的例子中就是一个程序员想重定向一个命令的输出。


import java.util.*;
import java.io.*;

// StreamGobbler omitted for brevity

public class BadWinRedirect
{
public static void main(String args[])
{
try

Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("java jecho 'Hello World' > test.txt");
// any error message?
StreamGobbler errorGobbler = new 
StreamGobbler(proc.getErrorStream(), "ERROR"); 

// any output?
StreamGobbler outputGobbler = new 
StreamGobbler(proc.getInputStream(), "OUTPUT");

// kick them off
errorGobbler.start();
outputGobbler.start();

// any error???
int exitVal = proc.waitFor();
System.out.println("ExitValue: " + exitVal); 
} catch (Throwable t)
{
t.printStackTrace();
}
}
}

Running BadWinRedirect produces: 


E:classescomjavaworldjpitfallsarticle2>java BadWinRedirect
OUTPUT>'Hello World' > test.txt
ExitValue: 0

程序员的本意是将Hello World这个输入重订向到一个文本文件中,但是这个文件并没有生成,jecho仅仅是将命令行中的参数输出到标准输出中,用户觉得可以像dos中重定向一样将输出重定向到一个文件中,但这并不能实现,用户错误的将exec认为是一个shell解释器,但它并不是,如果你想将一个程序的输出重定向到其他的程序中,你必须用程序来实现他。可用java.io中的包。


import java.util.*;
import java.io.*;

class StreamGobbler extends Thread
{
InputStream is;
String type;
OutputStream os;

StreamGobbler(InputStream is, String type)
{
this(is, type, null);
}

StreamGobbler(InputStream is, String type, OutputStream redirect)
{
this.is = is;
this.type = type;
this.os = redirect;
}

public void run()
{
try
{
PrintWriter pw = null;
if (os != null)
pw = new PrintWriter(os);

InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line=null;
while ( (line = br.readLine()) != null)
{
if (pw != null)
pw.println(line);
System.out.println(type + ">" + line); 
}
if (pw != null)
pw.flush();
} catch (IOException ioe)
{
ioe.printStackTrace(); 
}
}
}

public class GoodWinRedirect
{
public static void main(String args[])
{
if (args.length < 1)
{
System.out.println("USAGE java GoodWinRedirect <outputfile>");
System.exit(1);
}

try

FileOutputStream fos = new FileOutputStream(args[0]);
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("java jecho 'Hello World'");
// any error message?
StreamGobbler errorGobbler = new 
StreamGobbler(proc.getErrorStream(), "ERROR"); 

// any output?
StreamGobbler outputGobbler = new 
StreamGobbler(proc.getInputStream(), "OUTPUT", fos);

// kick them off
errorGobbler.start();
outputGobbler.start();

// any error???
int exitVal = proc.waitFor();
System.out.println("ExitValue: " + exitVal);
fos.flush();
fos.close(); 
} catch (Throwable t)
{
t.printStackTrace();
}
}
}

Running GoodWinRedirect produces: 


E:classescomjavaworldjpitfallsarticle2>java GoodWinRedirect test.txt
OUTPUT>'Hello World'
ExitValue: 0

这里就不多说了,看看就明白,紧接着作者给出了一个监测命令的小程序

import java.util.*;
import java.io.*;

// class StreamGobbler omitted for brevity

public class TestExec
{
public static void main(String args[])
{
if (args.length < 1)
{
System.out.println("USAGE: java TestExec "cmd"");
System.exit(1);
}

try
{
String cmd = args[0];
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(cmd);

// any error message?
StreamGobbler errorGobbler = new 
StreamGobbler(proc.getErrorStream(), "ERR"); 

// any output?
StreamGobbler outputGobbler = new 
StreamGobbler(proc.getInputStream(), "OUT");

// kick them off
errorGobbler.start();
outputGobbler.start();

// any error???
int exitVal = proc.waitFor();
System.out.println("ExitValue: " + exitVal);
} catch (Throwable t)
{
t.printStackTrace();
}
}
}

对这个程序进行运行: 
E:classescomjavaworldjpitfallsarticle2>java TestExec "e:javadocsindex.html"
java.io.IOException: CreateProcess: e:javadocsindex.html error=193
at java.lang.Win32Process.create(Native Method)
at java.lang.Win32Process.<init>(Unknown Source)
at java.lang.Runtime.execInternal(Native Method)
at java.lang.Runtime.exec(Unknown Source)
at java.lang.Runtime.exec(Unknown Source)
at java.lang.Runtime.exec(Unknown Source)
at java.lang.Runtime.exec(Unknown Source)
at TestExec.main(TestExec.java:45)

193在windows中是说这不是一个win32程序,这说明路径中找不到这个网页的关联程序,下面作者决定用一个绝对路径来试一下。

E:classescomjavaworldjpitfallsarticle2>java TestExec 
"e:program filesnetscapeprogramnetscape.exe e:javadocsindex.html"
ExitValue: 0


好用了,这个我也试了一下,用的是IE。


最后,作者总结了几条规则,防止我们在进行Runtime.exec()调用时出现错误。


<!--[if !supportLists]-->1、 <!--[endif]-->在一个外部进程执行完之前你不能得到他的退出状态

<!--[if !supportLists]-->2、 <!--[endif]-->在你的外部程序开始执行的时候你必须马上控制输入、输出、出错这些流。

<!--[if !supportLists]-->3、 <!--[endif]-->你必须用Runtime.exec()去执行程序

<!--[if !supportLists]-->4、 <!--[endif]-->你不能象命令行一样使用Runtime.exec()。

 

转自:http://hi.baidu.com/linnianda/blog/item/0f384a900fd1138da877a469.html

2月1日

中国式骂人

最近经常潜水于猫眼上看一大群自称的“左派”和“右派”之间相互攻击,相互诋毁。虽然主观上我比较倾向于那些所谓的“自由斗士”,但还是觉得双方对骂是在没有什么技术含量,往往某个楼主莫名其妙的一顿以偏概全的观点论述后,后边的跟贴不超过3(包括3)条就成了赤裸裸的对对方家人的激烈问候了。所以除了看点热闹外,实在难以得到许多的实际意义。不过看的多了,也发现我们骂人的方式,实在不怎么高明。

       我们传统上总是认为,一个人的人格有问题,则他的所有话语都是有问题的。所以在反驳对方时,并不是就事论事,总要先在人格上否定对方,所以就认定对方的观点的不正确。简单地说就是:你是混蛋,所以你的话都是混蛋话,所以我的话就都是正确的。简单的人身攻击成为了观点论述的核心部分,实在缺少一些技术含量。而且对于对方人格的侮辱,我们的方法也不外乎两种:

一.              无限上纲上线.比如说我,就经常被人以以下的推理攻击:你说中国人人性的缺陷,〈=〉你侮辱中国人〈=〉你是汉奸卖国贼〈=〉你根本就不是一个中国人〈=〉你没有资格评论中国人〈=〉中国人是没有任何缺点的无限完美的终极物种,推理完毕。实在说不清是文革培养了这种思考方式,还是这种固有的思考方式决定了文革。想想在那十年中,任何一个细小的玩笑都有可能被无限理解为对伟大领袖的背叛,从而背离了人民,成了人民公敌而被“专政”掉。而且在中国人的眼中,似乎“汉奸卖国贼”就是最终极的罪行,只要你能够想方设法把对手的言论归结到这一点上,就战无不胜了。

二.              人格细节侮辱. 《笑傲江湖》中任我行杀了东方不败,出来聆听到日月教徒的歌颂,这群人就开始疯狂攻击东方,首要大罪当然是背叛教主,谋朝篡位了,后边就是对于这个人的攻击,生活腐化,草菅人命就算了,还生生的给一个自宫练武的人加上种种淫贱的罪行,无怪令狐冲要哈哈大笑了。在总的问题没有什么大的毛病,或者在总的方向问题之外,我们一定要找到他生活细节上弱点。某年某月某日提过寡妇门之类的都可以成为肆意攻击的武器。人总是犯过错的,就是找不到也可以编一个,所以这一招几乎是无往不利的。

我见过的将这些手法用的最好的,就是陈光成的案件。把一个目盲的农民说成里通外国,泄露国家机密;两个律师一个被告偷钱包,一个被告骚扰妇女,两种手段完美结合,实在是“代表”了广大民群众智慧的结晶.

(转)章诒和的抗议引发的杂思——文军

    出差深圳,商务活动之余,抽空也上了网,阅读各种文章。禁书问题看来余波未尽,国家出版总署那位邬书林先生“因人废书”之议论看来已经引起众怒,他显然忽视了民意之主流表达平台,忘记了网络之存在。很多人感觉奇怪,这位邬副署长何以敢于宣布剥夺章诒和的出版权利?他显然可以做到“闷杀”,即不必说明原委就封掉章氏等人出书的权利,半个多世纪以来,不明不白扼杀掉的作品数不胜数。故,在以权谋为处事之本的官场里,这位邬君一定不是称职之官员。然而他那种傲慢,又确确实实体现了几千年来的官场气势,这个气势源自藐视一切法律制约的习惯。又有当事人出面澄清,说邬先生根本没有说过那样的话,只是并不影响章老太抗议的意义。

    毛曾经说:“无法无天”,那只是对他本人而言;对下边的官员,则需“无法有天”。所谓“天”,即自上而下的指令。有人抱怨我们对伟大领袖毛主席不够尊重,这项指责无疑是准确的,我们也曾经无限信仰、无限崇拜毛主席。但历史证明我们错了,国家、民族都蒙受了前所未有的灾难。我们愿意忘记这一切,只当所有的灾难都没有发生过,就像邓公说的,“一切朝前看。”儿子当初曾经讥笑过我:“老爸,你们当初怎么会那么傻?”确实无颜以对,不如忘记更好。

    然而人家不依不饶,连篇累牍地编造所谓过去的辉煌,继续大张旗鼓地宣扬谎言。这就不是过去的问题了,是现在的谎言。难道只许厚颜无耻的谎言充斥传媒,就不许别人哼一两句真话?鲁迅在《我要骗人》里说:“中国的人民,是常用自己的血,去洗权力者的手,使他又变成洁净的人物的。”鲁迅这话之主体不确,不是人民用自己的血去洗权力者的手,而是那些奴才文人用人民的血去洗权力者的手,顺便赚一点俸禄和润笔。又不错,“毛主席领导咱打江山”,而且大获全胜。只不过自“秦王扫六合”开始,历朝历代总有人打得下江山,唐宗宋祖成吉思汗,朱元璋努尔哈赤,都曾经是胜利者。莫文骅将军曾经鼓励家乡的市长大胆干,说:“那时候红军艰难得很,毛主席就瞎搞,七搞八搞,咦,还真的给他搞成了!”张闻天也称:“老毛谙熟旧社会那一套”,使用的是“非布尔什维克手段”。都可算“无法无天”之注脚。

然而中国要进步,靠这种“七搞八搞”是不行的。郎咸平曾经抨击中国官方发言人信誓旦旦说人民币不会升值,没几天就升值5%。郎咸平说,这是政府自己“以破坏政府信用为背书条件”,罪责是“缺乏信托责任”。不过郎教授精通经济或许不假,但他对于历史则纯属一派胡言。自打毛政府建立以来,信用从来如同厕纸,是随揩随扔的。试想,毛主席从来以权谋闻名天下,树立起至高无上的权力地位之后,更是肆无忌惮地指鹿为马信口雌黄。毛主席的形象继续光辉下去,中国社会必将毫无诚信可言,法律的尊严也只是一句空话。只要这个信仰仍被维护,掌控这个政府的人不可能具有公信力。于是,邬副署长及其所代表的权力机构无视《宪法》的决定与表态也就毫不奇怪。

    自古以来,中国从来不缺“依法治国”,且不论春秋战国就有“法家”,唐宋元明清,各有各的律法,甚至还传出“王子犯法与庶民同罪”之美谈。只是中国历来只有王法而无约法,王法就是朝廷制定、官府执行、百姓遵守的法律,可以朝令夕改的,只要对朝廷有利。而约法则不然,它自下而上,必须经过严格的民意表达程序始得以制定或修改,且必须严格执法。王法维系统治的权力,约法制约统治的权力,二者针锋相对,不可同日而语。《宪法》即是美国佬发明的约法,它肇始于《五月花号公约》,沿袭的却是英国古老的议会制度。说二十世纪是美国世纪,一个标识就是全世界都玩起《宪法》来了,就连王法也不得不用《宪法》时装来包装自己。当然不会玩真格的,那样会有“哈美”之嫌。

    无论怎样看待章诒和本人,她的抗议信都博得一片喝彩,因为她讨要的不光是《宪法》赋予的权利,而且争取《宪法》自身的尊严。记得1982年《宪法》草案付诸讨论之时,我们厂里干部学习讨论,工人出身的机修车间支部书记提出一个问题:“宪法大还是红头文件大?”惹来与会干部们一片笑声,厂总支书记笑得眼泪水都出来了:“我说老伍啊,你是真糊涂还是假糊涂?这不是明摆着嘛,来一个红头文件,宪法就废掉重改了,不是红头文件大难道还是宪法大?”那时,俺还吃着皇粮,绝无如今这份闲情逸致去与书记大人争辩。何况,书记说的是大实话,既然强调“一元化”领导,我也不知道“依法治国”将依据王法还是宪法。

    孟子.藤文公上》云:“民之为道也,有恒产者有恒心,无恒产者无恒心。苟无恒心,放僻邪侈,无不为已。”当“公有制”即列宁所定义的“国家垄断资本主义”剥夺了全体公民的一切,“放僻邪侈”就无可避免,不仅文革暴行的演绎可以证明,亚非拉那些“民无恒产”的国家可以证明,就连欧美那些发达国家上世纪前二三十年的历史同样也可以证明。法律在“无恒产”面前是苍白的。我下乡受到贫下中农的教育之一就是:“偷公家的不谓偷。”本生产队长决心发展集体经济,养了六七十只小鸡。到这些鸡长成快下蛋的时候,这群鸡迅速被盗,除队长会计外全队的老社员都偷。队长天天骂,鸡天天少,直至剩下七只时,队长干脆以14元的价格全卖给我了。还是在那个鸡舍,那七只鸡天天为我产蛋,却再也没被偷过。

    也许是意识到孟子的教导,邓公改革开放开了一个口子,老百姓或曰人民的开始被准许有一些“恒产”,例如房子、车子。尽管那房子还只有“产权”,这个具有中国特色的“产权”其实还只是使用权,不是所有权,因为房子脚下的土地只是借用五十年或七十年罢了,所谓“皮之不存毛将焉附?”且乡下农民的“土地承包权”也只是三十年而已。届时土地使用权满期,附于其上的房子便不知所以。尽管如此,百姓购房热情不减,弄得房价飞涨,买不起房的便怨声载道,“恒产”之魅力可见一斑。跟民意抱怨各类权利缺失相呼应,章老太对于权力自爽其约提出抗议,我想,这种维权行动应当是正义的。可是当初,岂止是书被禁,就连人被囚,章诒和有过提抗议的念头吗?

    法制是民主的需求,二者相辅相成,缺一不可。民主不是东西,而是百姓的政治权利。它当然不可能“是东西”如波音飞机那样从美国买回来。那些“不能照搬西方民主模式”之类说道,如果不是愚蠢到家的呓语就一定是聪明到顶的蛊惑。当老百姓期盼或感恩着“大救星”之时,民主只是天方夜谭;当老百姓为自己的权利呐喊甚至抗争时,民主进程便开始了。即便美国也不例外,那里的平头百姓两百多年来从未间断过为自己的权利呐喊、抗争,至今仍在进行中。这个进程才能唤作“美国式民主”,而不是一个有形的“民主馅饼”。因此,根本不存在什么可以“照搬”的“模式”。

    2007年,开门见山就迎来些热闹,除了禁书这件事以及章诒和提出抗议之外,还有报道说,已经禁了对于那次“引蛇出洞”五十周年的纪念。又是一个不许明白的举措。五十多年以来的历史,始终是一笔糊涂账。郑板桥那个条幅:“难得糊涂”,据说上个十年中国的处长以上干部大抵愿意压在玻璃板下面,以为座右铭或训诫,又据说现在已经不那么时髦了。现在似乎应该换一幅“难得明白”才是。毛时代,谁的书要是被禁了,不但饭碗要打破,还得连带批斗乃至殃及亲友的。即便象章诒和老父那样留作样板的高官,也只能唯唯诺诺谢皇恩浩荡。不管怎么说,章诒和至少比她爹那代人自在许多,敢提抗议了,谁说不是一种进步呢?

 

                                                                    作者: 文军 2007年01月30日, 星期二 21:54

http://publishblog.blogdriver.com/blog/tb.b?diaryID=1260440