博客搬家

2010年12月11日 ahei 没有评论

yo2是我真正使用的第一个博客, 我挺喜欢的, 可是自从去年yo2被关闭后, 我不得不重新考虑新的博客, 最终自己决定租了个虚拟空间, 买了个域名. 关于Emacs方面的内容, 可以访问Emacs中文网, 这个网站是我专门用于和大家一起探讨Emacs使用心得的, 关于其他方面的技术文章, 可以访问我的个人博客. 最后谢谢郭嘉, 谢谢yo2, 谢谢大家.

分类: 我的生活 标签:

w3m的小bug: w3m-auto-show

2009年12月25日 没有评论

好久没去上水木了, 今天上去瞧瞧. 本来以为几天不去了, 会有好多帖子, 上去之后, 只有一点帖子, 有点失望, 呵呵.

废话少说, 看到一个网友的问题:
求帮看看这句话为什么老有错误信息

(define-prefix-command 'f9-map)
(global-set-key (kbd "") 'f9-map)
(global-set-key (kbd " 3")
        (lambda() (interactive)
        (w3m-goto-url
"http://www.tianya.cn/publicforum/articleslist/0/no05.shtml")))
目的是想按f9 3, Emacs会调w3m去打开某个页面. 现在能work, 但老是提示error 出错信息是: Error in post-command-hook: (wrong-type-argument symbolp (lambda nil (interactive) (w3m-goto-url http://www.tianya.cn/publicforum/articleslist/0/no05.shtml)))
不知哪个兄弟有时间帮看下, 谢了~

我去emacs scratch下eval:

(define-prefix-command 'f9-map)
(global-set-key (kbd "") 'f9-map)
(global-set-key (kbd " 3")
        (lambda() (interactive)
        (w3m-goto-url
"http://www.tianya.cn/publicforum/articleslist/0/no05.shtml")))

然后按了F9 3, 果然如这位网友所说, 能打开那个网页, 但是出现那个post-command-hook那个错误.

很奇怪, 这段代码没问题啊, 怎么会出错呢?

我抱着试试的态度, 看把那个lambda表达式改为命令行不行, 即把

(lambda() (interactive)
  (w3m-goto-url
  "http://www.tianya.cn/publicforum/articleslist/0/no05.shtml"))

改为

(defun test() (interactive)
(w3m-goto-url
"http://www.tianya.cn/publicforum/articleslist/0/no05.shtml"))

然后再:

(global-set-key (kbd " 3") 'test)

这时, 再按F9 3已经可以正确运行了. 我再这样:

(global-set-key (kbd "C-x M-p") 'test)

然后按C-x M-p也没有错误, 看来很有可能是那个lambda表达式惹的祸.

还有, 报错是与post-command-hook相关的. post-command-hook作用如下(C-h v post-command-hook):

Normal hook run after each command is executed.
If an unhandled error happens in running this hook,
the hook value is set to nil, since otherwise the error
might happen repeatedly and make Emacs nonfunctional.

这个hook就是每个命令执行完之后都会运行这个hook中的函数, 如果某个函数运行出错后, 这个hook讲被置为nil.(与其对应的pre-command-hook, 即每个命令执行前执行的hook)
按F9 3出错后, 我去那个w3m buffer去查看了一下post-command-hook的值, 确实已经变为nil了, 看来错误应该与它有关. 所以我就去w3m的代码里搜索了一下post-command-hook, 结果如下:

-*- mode: grep; default-directory: "~/emacs/trunk/lisps/emacs-w3m/" -*-
Grep started at Thu Dec 24 22:51:38
find . -type f ! -wholename "*/.svn*" ! -wholename "*~" -print0 | xargs -0 -e grep -nH -e post-command-hook ./doc/emacs-w3m-ja.texi:3753 :o f @code{post-command-hook}. ./doc/emacs-w3m.texi:3699 :o f @code{post-command-hook}. ./w3m.el:1023:way of `post-command-hook'." ./w3m.el:8767:`post-command-hook' by `w3m-buffer-setup'." ./w3m.el:8788: (w3m-add-local-hook 'post-command-hook 'w3m-check-current-position) ./mime-w3m.el:90:by way of `post-command-hook'." ./mime-w3m.el:190: (w3m-add-local-hook 'post-command-hook ./ChangeLog.1:192: `post-command-hook', instead of `w3m-print-this-url-after-command'.
Grep finished (matches found) at Thu Dec 24 22:51:39

打开那些文件对应行, 与post-command-hook有关系的也只有w3m-after-cursor-move-hook,w3m-check-current-position, mime-w3m-after-cursor-move-hook和mime-w3m-check-current-position了. w3m-after-cursor-move-hook的值为:

'(w3m-highlight-current-anchor
  w3m-print-this-url
  w3m-auto-show)

mime-w3m-after-cursor-move-hook的值为:

'(w3m-print-this-url)

我大致看了下这几个函数, 没看出什么问题来. 关键也没看出与刚才那个lambda表达式相关的信息, 太奇怪了, 遂决定调试一下那个w3m-goto-url命令, 看它有没有问题.

在w3m-goto-url命令里面执行M-x edebug-defun, 对w3m-goto-url命令设置断点. 然后按F9 3, 在w3m-goto-url里面短住, 我不停的按n, 一步一步的调, 始终没出错, 等运行完这个命令才出错, 看来是与w3m-goto-url无关, 还是那个post-command-hook的问题.

我现在没辙了, 突然想到, 刚才不是说可能与那个w3m-after-cursor-move-hook有关嘛, 这个hook是在w3m-check-current-position这个函数中调用的, 而w3m-check-current-position又是在w3m-buffer-setup中调用的, 而w3m-goto-url里面又调用了w3m-buffer-setup, 看来确实有可能与w3m-after-cursor-move-hook这个hook有关. 那么我先把它置为nil试试, 一试, 太好了, 果然不出错了, 看来肯定是它的问题了, 它的默认值是:

'(w3m-highlight-current-anchor
w3m-print-this-url
w3m-auto-show)

我一个一个的试, 最后发现只要加上w3m-auto-show, 就出错, 看来问题在w3m-auto-show这个函数, 我们来看看它的代码:

(defun w3m-auto-show ()
  "Scroll horizontally so that the point is visible."
  (when (and truncate-lines
             w3m-auto-show
             (not w3m-horizontal-scroll-done)
             (not (and (eq last-command this-command)
                       (or (eq (point) (point-min))
                           (eq (point) (point-max)))))
             (or (memq this-command '(beginning-of-buffer end-of-buffer))
                 (string-match "\\`i?search-" (symbol-name this-command))
                 (and (markerp (nth 1 w3m-current-position))
                      (markerp (nth 2 w3m-current-position))
                      (>= (point)
                          (marker-position (nth 1 w3m-current-position)))
                      (< = (point)                        (marker-position (nth 2 w3m-current-position))))))      (w3m-horizontal-on-screen))    (setq w3m-horizontal-scroll-done nil))  

咋看上去没啥问题, 那么调试吧. M-x edebug-defun设置断点, 然后按F9 3, 停在w3m-auto-show这个函数里, 按n单步前进, 当运行到这行代码:

(string-match "\\`i?search-" (symbol-name this-command))

出错了. 这句话有什么问题吗? this-command是当前运行的命令, 我看了一下它的值是:

(lambda nil (interactive) (w3m-goto-url "http://www.tianya.cn/publicforum/articleslist/0/no05.shtml"))

然后我们再回头来看看那个错误提示:

Error in post-command-hook: (wrong-type-argument symbolp (lambda nil (interactive) (w3m-goto-url http://www.tianya.cn/publicforum/articleslist/0/no05.shtml)))

你发现了什么没? 错误提示的意思说那个lambda表达式不是一个符号. 真的是这样吗? 我们来试试. 在"*scratch*"里输入:

(symbolp (lambda (message "")))

然后在上面行尾按C-x C-e进行计算, 返回nil, 果然如此.

终于真莫道不消魂相大白了, 因为那个lambda表达式不是一个符号, 而symbol-name的参数要求为符号, 所以才产生了那个错误. 看来这是w3m的一个小小的bug.

那么我们怎么来fix这个小bug呢?
很简单, 在调用symbol-name之前判断一下即可, 即把

(string-match "\\`i?search-" (symbol-name this-command))

改为

(string-match "\\`i?search-"
              (if (symbolp this-command) (symbol-name this-command) ""))

就可以了. 很简单吧. :)

终于注册了一个独立域名

2009年12月19日 7 条评论

前阵子, 有朋友建议我注册个独立域名, 买个空间, 那样会稳定些. 昨天同事说godaddy圣诞节前, 搞优惠活动, 申请.com域名时, 只要输入优惠码“BUYCOM99”, 就可以享受0.99美元的优惠价格. 遂也打算去注册一个域名. 先试了下emacs.com, 已被注册, 然后再试了下emacser.com, 没被注册, 太好了. 赶快注册!

注册的时候, 考虑支付方式的时候, 由于看到月光博客上说godaddy会不经本人同意直接刷你信用卡上的钱, 心有余悸, 所以准备用paypal支付. 立马去注册了一个paypal账号, 刚注册完, 手机收到短信说我的尾数为****的信用卡消费1美元, nnd, 虽然已经听同事说过paypal会刷一点钱, 来验证一下信用卡的有效性, 但还是不爽. 算了, 继续注册我的域名. 等到选择支付方式的时候, 找了半天, 也没找到可以选择paypal进行支付, 我Google之, 无果. 据说现在godaddy也支持支付宝支付, 也Google了半天无果. 真个郁闷, 那我刚才注册的paypal岂不是没用? 白花了1美元. 算了, 狠狠心, 就刷信用卡吧. 刷完后, 域名搞到手, 开始域名转向, 转到我的博客. 据说域名转向要一两个小时, 我就等啊等. 一个下午过去了, 我通过我们公司的国外的服务器能访问我的域名了, 但是国内还是不行, 郁闷.

晚上回来, 就查了一下godaddy域名转向的问题, 原来godaddy的域名转向被棺材店封了, 该死的棺材店! 不过棺材店永远也斗不过"有着雪亮的眼睛但却不明真莫道不消魂相"的群众, 找到了一篇godaddy域名转向的文章, 终于搞定了, 你现在已经能通过http://emacser.com来访问我的博客了.

最后, 我不服气为啥paypal非要扣我一美元, 去Google了一把, 原来是这么回事, 还是感觉不爽, 支付宝关联银半夜凉初透行卡的时候, 给我们打几分钱过来, 它却倒好, 给我们扣个2.95美元, nnd. 不过今天去给paypal客服打电话, 客服mm态度不错, 不过有点像机器人, :) .

用tsocks代替sockscap来转发网络请求

2009年12月7日 2 条评论

你有没有遇到过这种情况: 某一台机器A的网速特别快, 另外一台机器B和A机器在同一个局域网内, 但是B机器的带宽有限, 由于A机器和B机器由于是在局域网内, 传输速度很快, 所以如果能把B机器的网络请求先发到A, 再由A转发出去, 这样B机器的网速可以一样很快了. 那么怎样来转发请求呢? 很显然, 用代理软件就可以做到. 但是, 我在这里给大家提供一个更简便的架设socks代理的方法, 用ssh服务.

ssh的功能巨强大, 大家可以通过它的man详细了解它的功能, 它的man非常详细. 利用ssh架设代理服务主要是利用它的"-D"选项, 这个选项后面跟一个ip地址和端口, 格式为ip:port, ip地址为你本机待绑定的ip, 是可选的, 加了这个选项后, 就表示在本机与目标机器之间建立一条ssh通道, 而且在本机监瑞脑消金兽听一个你指定的端口.
我写了一个简单的小函数来开通ssh的代理:

genproxy ()
{
    ip="$1";
    user="$2";
    ssh -o StrictHostKeyChecking=no "$ip" -l "$user" -D 8888 -N -f
}

这个函数的第一个参数是ip, 第二个参数是用户名, "-o StrictHostKeyChecking=no"表示目标主机的key未知或者改变过时, 不提示. 如果不加这个选项, 你可能会得到类似以下的提示:

The authenticity of host '172.0.1.251 (172.0.1.251)' can't be established.
RSA key fingerprint is 51:18:fe:5f:de:a7:55:ef:7c:d4:6e:ba:bc:9e:a2:7c.
Are you sure you want to continue connecting (yes/no)?

"-N"表示ssh到目标机器后不执行任何命令, 一般不加这个选项的话, 会连接上目标机器后执行你指定的命令, 如果你没有指定任何命令的话, 就直接执行/bin/sh, 所以这个"-N"通常会用在这种只需要监瑞脑消金兽听端口的场合.
"-f"表示在执行任何命令之前转入到后台进行处理.
上述命令如果执行成功的话, 用netstat能看到本机已经建立一个8888端口, 这时候, 只要有网络请求转发到8888这个端口, ssh会把这个请求通过刚才已经建立好的ssh通道发到目标机器上, 从而达到代理的作用. 那么怎么来把请求发到8888这个端口上呢? 有些软件, 比如qq, 有设置socks代理的功能, 但是有好多软件都没有设置socks代理的功能, 那么对于这些软件该怎么办呢? Windows下sockscap这样的软件, 你可以把一些软件的快捷方式加入到sockscap里面去, 然后要想使用代理的话, 就直接在sockscap里面来启动软件. 很方便. 那么linux下是否也有这类软件呢? 当然有, 而且更方便.

tsocks就是一款类似sockscap的网络请求转发的软件.
使用很简单, 安装完tsocks后, 打开它的配置文件/etc/tsocks.conf, 翻到文件末尾, 里面有一个server和server_port的选项, 这个就是socks server的ip和端口, 分别填上即可. 要注意的地方就是, 对于上面那个例子, 在B机器上配置tsocks的时候, server应该写127.0.0.1, 而不是A机器的ip, 因为8888端口是在B机器上开启的. 我今天配置的时候, 就犯了这个错误, tsocks提示"socks server is not on a local subnet local", 很诡异, 弄了半天才明白. 切记切记.
配置好后, 直接tsocks后面跟命令就可以了, 比如

tsocks wget http://www.g.cn

就可以使用代理来wget了. 但是这样的话, 就必须要在每个命令前都要加tsocks. 还有一个更简单的方法:

. tsocks -on

注意了, 上面这个命令前面有一个点号, 必须要的, 我今天在配置的时候, 也是没加, 弄了半天都不行, 后来仔细看了tsocks的man才知道了. 那么为什么要加点号呢?
shell里面,

./test.sh

shell会开启一个子shell进程来执行test.sh, test.sh里面所有影响环境变量的语句对它的父shell进程都没有影响, 而

. ./test.sh

不是单独开启一个shell进程, 而是在当前shell下执行test.sh, 这样test.sh里面对环境变量影响的语句在当前shell就起作用了.
知道上面加与不加点号的区别后, 我们再来看看tsocks的源码:

case "$1" in
        -on)
                if [ -z "$LD_PRELOAD" ]
                        then
                                export LD_PRELOAD="/usr/lib/libtsocks.so"
                        else
                                echo $LD_PRELOAD | grep -q "/usr/lib/libtsocks\.so" || \
                                export LD_PRELOAD="/usr/lib/libtsocks.so $LD_PRELOAD"
                fi
        ;;
        -off)
                export LD_PRELOAD=`echo -n $LD_PRELOAD | sed 's/\/usr\/lib\/libtsocks.so \?//'`
                if [ -z "$LD_PRELOAD" ]
                        then
                                unset LD_PRELOAD
                fi
        ;;
        -show|-sh)
                echo "LD_PRELOAD=\"$LD_PRELOAD\""
        ;;
        -h|-?)
      echo "$0: Please see tsocks(1) or read comment at top of $0"
   ;;
        *)
                if [ -z "$LD_PRELOAD" ]
                then
                        export LD_PRELOAD="/usr/lib/libtsocks.so"
                else
                        echo $LD_PRELOAD | grep -q "/usr/lib/libtsocks\.so" || \
                        export LD_PRELOAD="/usr/lib/libtsocks.so $LD_PRELOAD"
                fi
if [ $# = 0 ] then ${SHELL:-/bin/sh} fi
if [ $# -gt 0 ] then exec "$@" fi ;; esac

从上面可以看出, tsocks这个脚本是通过修改LD_PRELOAD这个环境变量来达到它的转发网络请求的目的. 那为什么修改LD_PRELOAD这个环境变量就能达到他的转发网络请求的目的呢? 说简单点, LD_PRELOAD这个环境变量表示系统会把这个变量对应的共享库文件中的函数来覆盖目标程序中的函数, 详情请看这里.
现在你应该明白了为什么tsoc -on前面为什么要加点号了吧? 你不加点号的话, tsocks脚本修改的LD_PRELOAD变量不对当前的shell进程起作用啊.

. tsocks -on

加到你的.bashrc里面, 这样每次打开新的shell会话都可以使用代理. 如果想所有的软件都使用代理的话, 包括不是在shell里面启动的, 重启一下机器, 使得你的.bashrc里面的配置对全局生效.

. tsocks -off

关闭tsocks,

. tsocks -sh

显示LD_PRELOAD的值.

Nutch配置文件的加载

2009年11月30日 没有评论

Nutch的配置文件主要有三类:

  • Nutch插件的配置文件,这些配置文件主要是在加载插件的时候由插件自己加载的,主要是filter和normalizer插件的配置文件
  • Nutch自己的配置文件,nutch-default.xml和nutch-site.xml
  • Hadoop的配置文件,hadoop-default.xml和hadoop-site.xml

这些配置文件的加载顺序决定了它们的优先级,优先级低的会被优先级高的配置文件中的配置覆盖,所以要想配置好nutch,了解配置文件的加载顺序是必须的。下面我通过对nutch源码的剖析来看看nutch是怎样加载配置文件的。

Nutch的主要命令是"./nutch crawl",而这个crawl命令main类是org/apache/nutch/crawl/Crawl.java,我们就从Crawl.java的main方法开始。

Nutch配置文件的加载主要是以下代码:

  /* Perform complete crawling and indexing given a set of root urls. */
  public static void main(String args[]) throws Exception {
    if (args.length < 1)
    {
      System.out.println("Usage: Crawl  [-dir d] [-threads n] [-depth i] [-topN N] [-r]");
      System.out.println("-rtremove css and javascript, default is do not remove");
      return;
    }
Configuration conf = NutchConfiguration.create(); conf.addResource("crawl-tool.xml"); JobConf job = new NutchJob(conf);

上述代码中,"Configuration conf = NutchConfiguration.create();"生成一个NutchConfiguration的对象,NutchConfiguration是管理Nutch自己的配置文件的类,Configuration类是管理Hadoop配置文件的类,我们进入create方法:

  /** Create a {@link Configuration} for Nutch. */
  public static Configuration create() {
    Configuration conf = new Configuration();
    addNutchResources(conf);
    return conf;
  }

create方法中先创建一个Configuration对象,Configuration方法如下:

  /** A new configuration. */
  public Configuration() {
    this(true);
  }
/** A new configuration where the behavior of reading from the default * resources can be turned off. * * If the parameter {@code loadDefaults} is false, the new instance * will not load resources from the default files. * @param loadDefaults specifies whether to load from the default files */ public Configuration(boolean loadDefaults) { if (LOG.isDebugEnabled()) { LOG.debug(StringUtils.stringifyException(new IOException("config()"))); } if (loadDefaults) { resources.add("hadoop-default.xml"); resources.add("hadoop-site.xml"); } }

由此可见,当构造Configuration对象的时候,会先去加载hadoop-default.xml,然后再去加载hadoop-site.xml,所以hadoop-site.xml里面的配置会覆盖hadoop-default.xml里面的配置。
了解了Hadoop的配置文件的加载,我们再回到刚才的create方法里面。
现在要调用“addNutchResources(conf);”了,其定义如下:

  /** Add the standard Nutch resources to {@link Configuration}. */
  public static Configuration addNutchResources(Configuration conf) {
    conf.addResource("nutch-default.xml");
    conf.addResource("nutch-site.xml");
    return conf;
  }

这里很明显看出,先加载nutch-default.xml文件,然后再加载nutch-site.xml文件。
下面我们再沿着main方法继续往下看,该到调用“conf.addResource("crawl-tool.xml");”了,看来crawl-tool.xml最后加载,这个配置文件主要是用于配置抓取企业内部网。

通过我们上面简单的源码分析,我们得出Nutch配置文件的优先级为:

  • hadoop-site.xml要高于hadoop-default.xml
  • crawl-tool.xml高于nutch-site.xml,nutch-site.xml高于nutch-default.xml

致Emacs初学者

2009年11月30日 10 条评论

需要专门花时间去学的软件为数不多, Emacs正是其中之一. 我周围的好多人在我的"鼓吹"下, 也对Emacs感兴趣起来, 可是不过好久, 就放弃了, 究其原因, 我想是他们对Emacs的认识不够, 或者是学习方法不正确. 在这里, 我想说一下Emacs初学者应该注意的一些问题.

Emacs是什么

首先, 我想你应该要知道Emacs是什么, 不知道的同志请看这里.

你为啥要学Emacs

  • 赶时髦
    那我劝你还是别学了, 学Emacs可赶不起时髦, 用Emacs的人可少了. 我呆过的三个公司, 都是在linux下做开发的, 但每个公司都只有2,3个人用. 我一个同事告诉我, 他认识的人中只有我一个人用Emacs. 而且你不但赶不起时髦, 还很有可能被人"鄙视". 我曾经向一位朋友推荐Emacs, 说Emacs是一款非常强大的工具. 他使用了后, 告诉我, Emacs太土了, 删之. 看来从古到今, 相貌永远起着不小的作用. 我还有一个同事, 问我用什么工具开发, 我说用Emacs, 他说, 太土了, 远古时期的工具, 用Eclipse吧. 我当时只有一种感觉, 我比窦娥还冤. 本曾想他夸我一句, "You are cool!", 没曾想, ..., 伤心往事.
  • 装酷
    嗯, 首先得承认在这个大环境中, 用Emacs确实很酷. 在这个菜单很少, 鼠标动作很少, 没有漂亮的界面, 只有一个黑乎乎的文本框的软件里, 完全用键盘操作, 多酷多拉风啊. 可是, 酷虽酷, 可不是一时半会能学会的, 得花时间去学, 得有毅力, 如果你没有这个毅力的话, 就此打住.
  • 想在特定语言的功能方面超过特定IDE
    比如, 你想在java语言方面, 针对java的特有的功能这一方面, 你想使用Emacs超过Eclipse, 那Emacs不太适合你. Emacs的年龄很大, 比我们这些年轻人的年龄都要大, 好多高级功能, 比如Eclipse的代码重构, 它就没有. 一般IDE都有的代码补全, 代码浏览, Emacs在这方面也做的不是很好, 当然这些方面, 都会有的, 因为Emacs的扩展性实在是太强大了, 任何人都可以扩展它, 那些功能只要用户需求大, 最终会有人写的. 既然这样, 那么Emacs的强项到底在哪里呢?

Emacs的强项

  • 无限的定制性
    萝卜青菜, 各有所爱. 人与人之间的习惯差别太大了. 一款工具怎么能适用所有人? 那可定制性是必不可少了. Emacs在这方面做的非常出色. 你可以对任何选项进行定制, 你可以定制任何按键, 你可以。。。.
  • 无限的扩展性
    如果你使用了一款扩展性不强的软件, 如果没有别的软件中某个特别好用的功能, 那么你只有期望软件作者能为你开发这一功能了. 如果用户都希望有这个功能的话, 也许你不用等多久作者就为用户开发了那个功能了. 但是如果只是一个你觉得好用的功能呢? 那怎么办? 恐怕作者不大可能为某个特定用户开发特定的功能. 这时候, 扩展性显得多么重要. Emacs在这方面也做的非常出色. 它以强大的Elisp语言作为扩展语言, 扩展性远远超过几乎任何一款软件(包括vim).

Emacs适合哪些人

  • 有区别于别人的使用习惯, 有一些自己的, 软件作者不会帮你实现的需求的人
  • 喜欢追求高效率操作的人. 如果你喜欢用鼠标点来点去, 如果你喜欢按那些难用的功能键, 那就不用再看了, Emacs不太适合你.
  • 喜欢折腾的人. 用Emacs是需要折腾精神的, 如果你是一个懒人, 不太想折腾, 恐怕Emacs不适合你, 想用Emacs不折腾, 不大可能.

在什么操作系统下使用Emacs

Emacs支持众多的操作系统, Windows也在其中, 但是如果想发挥Emacs的巨大威力的话, 建议你还是不要在Windows下使用Emacs, 说的不好听点, 在Windows下使用Emacs一是折磨你自己, 二是糟蹋Emacs, 因为Emacs用到好多第三方的工具, 比如w3m, 在*nix下安装都非常方便, 在Windows下都比较麻烦, 而且有的工具干脆就没有Windows版本. 我见到好多Emacs的初学者, 由于不熟练*nix, 选择在Windows下折腾Emacs, 什么HOME问题, 什么字体问题啊, 简直就是折腾不完的问题. 要是真的想学好Emacs的话, 必须要学好*nix, 而且这么好的操作系统, 熟练使用它也是一件幸事.

从哪开始

如果你能通过上面几条, 恭喜你, 你可以开始你的Emacs之旅了.
那么到底怎么学习Emacs呢? 我觉得刚开始学习Emacs的时候, 应该先了解一下Emacs的一些基本概念和基本知识(毕竟Emacs和其他的IDE的一些概念和理念是不同的), 比如, Emacs的buffer, window, frame, mode, Isearch. 知道这些最基础的概念之后, 应该再学习一下最基本的定制知识, 比如绑定快捷键啊, 定制选项啊, 还有看文档的方法, Emacs中的文档有函数自己的文档, 还有强大的info系统. 关于这些基础知识, 我推荐你去看《学习GNU Emacs》这本书, 非常适合初学者.

Emacs的考验

你现在是不是有点不想学Emacs了? 为啥?

  • 按键难按
    太冤枉Emacs了, Emacs的按键是最好按的, 你最好先把你的Control键和Caps lock键交换一下。
    最常用的是两个键的按键, 分别为Control和Alt键开头的, 其次是四个按键中的有两个按键为Control键的, 为啥四个按键比三个按键还好按? 比如C-x C-c, 这个是Emacs的退出键, 当你按C-x C-c的时候, 不需要先按C-x, 然后手拿起来, 再按C-c, 这样按当然难按, 你应该在按完C-x后, 按Control键的那个手指不需要厉离开 只需要用另外一个手指去按c就行了, 是不是只相当于按了三个按键? 你现在试试, 是不是更好按了些? 你再多试几次, 是不是非常方便? 是不是比两个键的按键差不到哪去?
    Emacs的按键设置的非常合理,默认的按键离键盘中心都很近,手指基本不需要离开键盘区,比windows下的按键不知道要好按多少倍,windows下的快捷键基本都是Control,Shift,功能键组合,须不知功能键离键盘中心那么远,多难按阿。
    个人觉得Emacs的按键也比vim的按键设置的合理,虽然vim的按键非常短,但是都是特别难按。比如回到行尾的$,回到第一个非空白字符的^,还有删除一个单词dw,试想阿,你如果想删除一系列单词怎么办?难道先要数数要删除多少个单词?然后用dNw?太慢了。要么就dw,dw,dw,。。。,按N次,这样就需要按2N次键,但是在Emacs下删除一个单词是M-d,你只需要按着Alt键一直不放,然后不停的按d就可以了,仅需要按N+1次键,方便之极。诸如此类的还有很多。所以如果你仅因为vim按键比较短,而转去学Emacs的话,趁早回头是岸吧,不要被短的按键迷惑了。我一个同事就这样被我从vim阵营拉过来了, :)
    其实上面所说的vim按键的那个问题, 可以通过"."来完美的解决. 但是其他的问题, 可能就稍微麻烦了点, 比如vim编辑的时候要不停的在编辑模式和命令模式来回切换, 当然可以通过映射来做到不切换, 但是映射的键太多, 而且映射完, 按键也基本和Emacs差不多了, :)
  • 平时工作太忙, 没时间学, 以后有空再学
    别再有这种想法了, 你会永远没空. 明日复明日, 明日何其多啊. 你什么时候工作会闲下来? 公司辞退你的时候吗? 我认识中的人中, 好多人一开始都对Emacs挺感兴趣的, 最后都是因为没时间, 与Emacs无缘. 现在的人, 有哪个不忙? 时间就像***(此处已被宇宙第一帝国FFF工程抹去数字,详情请致电FFF工程总部热线444-54545444), 挤挤总会有的. 要是真想学Emacs的话, 要加班去学Emacs. 等了解完Emacs的基本概念和基础知识后, 要在工作中去用Emacs, 不用Emacs你永远也学不会Emacs. 在用的过程中, 碰到问题, 晚上再继续学. 长此以往, 岂有不熟之理. 我都是靠晚上加班来学Emacs的.

如果你真的想学Emacs的话, 不要再找借口了. 一失足成千古恨, 再回首已百年身啊. 趁年轻喜欢折腾, 多折腾折腾, 不要等到老来少年之狂已成往事之时, 追悔莫及啊. 我以前的一个同事,他以前的公司同事都用vim,所以也用vim,自从我向他介绍了Emacs后,开始学Emacs,一开始也没觉得好用,但是他坚持下来了,越用越好用,最终觉得Emacs太好用了。他是第一个我传道Emacs成功的对象, :)

Emacs进阶

当你掌握了上述的基本概念和基础知识后, 恭喜你, 你将开始初学者的第二个阶段.
接下来, 你可以去网上找一些牛人的配置来看看, 比如王垠(清华的退学博士, 很佩服), 叶文斌(他的主页已经不在了, 我备份了一份, 可惜yo2的上传文件竟然不能超过2M,。。。,有需要的同志到关于页面找联系方式致电我), 王纯业(他的主页也不在了), pluskid(浙大的牛人, yasnippet的作者). 我刚开始学Emacs的时候, 基本上也是看他们的配置. 看到别人的配置中比较有意思的地方, 你再拷贝到你的配置中, 这样看多了, 你就慢慢熟悉Emacs了. 但是我建议不要直接拿别人的配置文件来使用, 毕竟别人的配置是别人的, 它包括了别人的使用习惯, 不一定适合你. 我的配置中就改了一些Emacs基本的快捷键, 比如`C-k'我改成了删除一行, 而不是删除光标到行尾, 这也许不适合你. 当然我对于那些尽量能提供给别人的配置我是单独拿出来做成一个包的, 比如我的颜色主题color-theme-ahei, 还有dired-lis(这是一个使得你在dired中直接输入字母跳到对应文件的包, 就像Total Commander中那样, 非常的好用). 你可以去直接使用这些包, 再经过你自己的配置, 就为你所用了.
初学者在配置Emacs的过程中, 我相信经常会遇到加了某个配置后, 以前某个能用的功能现在不能用了, 不用着急, 我有三条妙计为你排忧解难:

  • 当你的配置出现问题时, 在Emacs启动命令后增加参数"--debug-init", 这样启动后, 如果配置出错, Emacs会报出具体出错的位置, 这样你就可以很容易定位问题了.
  • 上述加参数的方法, 只适用于你的配置使得Emacs启动出现错误, 但是如果Emacs启动没有出现错误, 但是你需要的某个功能就是不能正常使用该怎么办呢? 很简单, 把你的配置用版本控制工具管理起来, 我的配置DEA就用svn管理起来, 记住, 用版本控制工具的时候, 一定要记得写日志, 方便以后出问题的时候, 容易找出问题, 我接触的人中, 好多人都没有写日志的习惯. 当你现在的版本出现问题时, 你可以看看以前的版本有没有问题, 找到一个没有问题的版本, 然后再用现在的版本和以前的版本比较一下, 看是修改了哪些配置, 然后再在这些配置上集中找问题.
  • 如果你嫌版本控制麻烦, 或者出问题的版本和不出问题的版本之间的差别很大的话, 还有一种方法. 排除法! 即不管你有多少条配置语句, 我一个一个的排除, 我先去掉第一条语句, 看有没有问题了, 没有啦? 恭喜你啦, 你试一次就试出来了. 还有? 也不用担心, 继续试. 你是不是觉得我这个方法似乎太傻了, 对于几行的配置文件, 这样是管用, 那么对于几百行, 甚至上千行的配置文件怎么办? 一个一个的排除? 太慢了吧? 别急, 我有二分调试法为你降妖附魔.

    二分调试法

    什么是二分调试法? 名字很酷吧? 我自己取的, :) 二分调试法首先本质上也是排除法, 其次, 二分嘛, 肯定与二分搜索有关系嘛, 对头, 它就是利用了二分搜索法的算法思想. 假如对于64行的配置文件, 现在出现某个问题了, 我首先把后面32行注释掉(用(when nil)注释, 方便快捷), 看看有没有问题:

    1. 没问题? 太好了, 那问题肯定是出在33行到64行之间, 继续二分, 把刚才的注释取消掉, 把48行到64行之间的代码注释掉, 继续判断.
    2. 有问题? 同样太好了, 那问题肯定是出在1到32行之间, 继续二分, 把刚才的注释取消掉, 把16行到32行之间的代码注释掉, 继续判断.

    经过以上递归判断, 最终肯定能找到问题所在. 好多问题我都是靠这种方法解决的.

    二分调试法效率如何

    二分二分嘛, 当然也和二分搜索法效率一样的了, 复杂度lgN, 所以甭管多大的配置文件, 二分调试法一会就能帮你找到问题所在. 100万行?哇,好大,20次搞定!算法的威力强大吧?!

    适用于其他语言吗?

    当然适用

    注意事项

    你不会傻的真的完全二分吧? 二分的时候对于代码块, 比如for, while等要放在一起, 不能分开.

成为Emacs高手

经过上述的锻炼, 我相信你现在应该可以熟练的使用Emacs了. 但是还不够, 为什么还不够?

  • 遇到问题怎么办
    除了Google, 或者向别人请教之外, 能不能自己来解决?
  • 想要实现一些Emacs没有的功能

这就需要你懂Elisp语言了.
学Elisp主要是看文档(废话), 一个是Emacs函数的自文档(M-x describe-function, 默认按键绑定是C-h f), 还有更强大的就是Elisp的info. 英文不好的同志(其实Emacs函数的自文档和info的英文都很简单)可以看看叶文斌的elisp教程,《GNU Emacs Lisp编程入门中文版》. 刚开始学Elisp的时候, 可以先看看上面的2个中文教程, 系统了解一下Elisp, 看了差不多后, 可以开始看看Emacs函数的自文档, 自文档非常的方便, 可以在看代码的时候, 随时遇到不懂的函数随时查看文档. 我写了两个查看Elisp自文档和代码非常方便的包, find-symbol, describe-symbol, 有兴趣的同志可以试试. 等到自文档看的比较熟练后, 可以看info了, info写的非常详细. 当然, 以上过程, 并不一定要循序渐进, 你也可以在看自文档的同时, 看看info.

高手之后

哈, 为Emacser们多写点方便实用的Elisp包吧!

Emacs才是世界上最强的IDE - 高亮光标处单词

2009年11月27日 6 条评论

Eclipse是一个比较不错的IDE(当然与Emacs比起来还是差不少, :) ), 现在用它的人挺多的, 用过的用户应该都会知道, 在Eclipse里面, 当把光标移到一个变量, 或者方法, 或者类名, 或者类型名上面的时候, Eclipse会把这个符号的所在的作用域中的所有这个符号都高亮, 比如局部变量abc, 它就会把当前作用域内所有的局部变量, 就像下面这个样子:

eclipse中高亮光标处单词

eclipse中高亮光标处单词

但是它并不会高亮与它同名的但是不同类型的单词, 比如它并不会高亮字符串"abc", 如上图所示. 它也不会高亮和它不在同一作用域内的单词, 比如上图中的下面那个大括号扩住的代码块内的abc, 虽然和上面的代码块的abc同类型, 但是不是同一作用域, Eclipse也不会高亮它, 甚至, Eclipse都不会高亮当前光标处的单词的重载方法(这点感觉不太方便), 如下图所示:

eclipse中高亮光标处单词

eclipse中高亮光标处单词

总之, Eclipse只会高亮当前作用域内和当前光标处单词完全是同一个语法个体的单词.

那么Emacs中是否有类似的功能呢, 当然有! highlight-symbol这个包就我们实现了这个功能, 我们先来瞧瞧它长啥样:

用highlight-symbol高亮光标处单词

用highlight-symbol高亮光标处单词

上图中那些灰色背景, 蓝色前景的"defconst"就是用highlight-symbol高亮的,漂亮吧, :) .

highlight-symbol与Eclipse的高亮功能有哪些不同点呢?

  1. 它除了自动高亮, 即随着光标的移动自动高亮所有当前光标下的单词, 还可以手动高亮, 比如我先高亮所有的abc, 然后再高亮所有的def, 还可以高亮所有的ghi, 还可以..., 总之, 可以无限高亮所有单词, 就像下面这样:

    emacs中用highlight-symbol高亮多个单词

    emacs中用highlight-symbol高亮多个单词

  2. 它不是以语法个体进行高亮的, 它仅以单词进行高亮, 即: 假如当前光标下的abc是int型, 它高亮的时候, 同时会高亮字符串"abc", 这样既有好处, 也有坏处, 好处就是我这样一眼能看清楚有多少当前光标下这样的单词, 而且不区分作用域(从上面的图也可以看出来), 因为它不做语法分析, 坏处就是不同类型的单词混在了一起.
  3. 它可以高亮任意类型的文件, c/c++/java/ruby, 等等, 包括text, 我就用它来高亮text. 看看这个:
    Eclipse恐怕只支持很少几种.
  4. 它可以在所有相同的单词之间跳转, 回到前一个, 退到后一个, 非常方便, 还可以只在一个函数内进行跳转.

总之, 有优点也有缺点, 不过优点明显多于缺点.

我觉得一个好的高亮当前光标处单词的工具应该把Eclipse和highlight-symbol的优点结合起来, 既可以根据语法进行高亮, 语法高亮的时候还可以由用户控制是否区分作用域, 还可以根据单词外形进行高亮.

下面我来讲讲highlight-symbol的使用.

C-c M-H高亮当前光标下的单词, C-c M-R取消所有单词的高亮, C-c M-N移到下一个高亮, C-c M-P则移到上一个高亮, C-c M-n在函数内移到下一个高亮, C-c M-p在函数内移到上一个高亮, C-c r对当前光标下的单词进行替换. 使用很简单吧.

注意:highlight-symbol与color moccur以及w3m冲突,当启用了`highlight-symbol-mode'后,moccur和w3m自己的颜色高亮就没了。

这里有我的highlight-symbol的配置文件, 配置如下:

;; -*- Emacs-Lisp -*-
;; Time-stamp: <2009-11-27 15:12:23 Friday by ahei>
(require 'highlight-symbol) (require 'sgml-mode)
(setq highlight-symbol-idle-delay 0.5)
(defun highlight-symbol-mode-on () "Turn on function `highlight-symbol-mode'." (highlight-symbol-mode 1))
(defun highlight-symbol-mode-off () "Turn off function `highlight-symbol-mode'." (highlight-symbol-mode -1))
(dolist (hook '(emacs-lisp-mode-hook lisp-interaction-mode-hook java-mode-hook c-mode-common-hook text-mode-hook ruby-mode-hook html-mode-hook)) (add-hook hook 'highlight-symbol-mode-on))
;;;###autoload (define-globalized-minor-mode global-highlight-symbol-mode highlight-symbol-mode highlight-symbol-mode-on)
;; I bind "C-x w" to `copy-sexp' (apply-define-key hi-lock-map `(("C-x w" nil)))
(dolist (map (list emacs-lisp-mode-map lisp-interaction-mode-map java-mode-map c-mode-base-map text-mode-map ruby-mode-map html-mode-map)) (apply-define-key map `(("C-c M-H" highlight-symbol-at-point) ("C-c M-R" highlight-symbol-remove-all) ("C-c M-N" highlight-symbol-next) ("C-c M-P" highlight-symbol-prev) ("C-c r" highlight-symbol-query-replace) ("C-c M-n" highlight-symbol-next-in-defun) ("C-c M-p" highlight-symbol-prev-in-defun))))

auto complete和yasnippet的区别

2009年11月27日 28 条评论

auto-complete和yasnippet是Emacs下两款非常强悍的补全插件,那么auto-complete和yasnippet是否就是一对竞争者呢?有你没我,有我没你?

其实不是这样的,它们两个完全能融洽的相处,并且合作的非常愉快。

本质上来说,auto complete只是一个补全界面,它用来展示其他补全引擎的结果,它支持好多补全引擎,包括补全全路径文件名的backend,补全单独文件名的backend,补全当前buffer下单词的backend,补全所有buffer下的单词的backend,补全Elisp语法的引擎,补全yasnippet片段的引擎,补全缩写的引擎,等等等等,当然也包括yasnippet,它会把所有的补全引擎的补全结果一起展示出来,当然是根据你定义的补全引擎的顺序。

那么我们为什么不直接使用yasnippet,而去使用auto complete呢?

除了auto complete可以同时利用其他补全引擎的结果,还使得使用yasnippet起来更方便。比如在yasnippet中你看不到当前光标出单词是否有它的片段定义,你要补全它的时候,要按tab,而auto complete加了yasnippet的引擎后,可以自动提示出当前光标处单词是否有yasnippet的片段定义,yasnippet引擎的结果会用特殊的颜色标注,这里有代码证明:

(defface ac-yasnippet-candidate-face
  '((t (:background "sandybrown" :foreground "black")))
  "Face for yasnippet candidate.")
(defface ac-yasnippet-selection-face '((t (:background "coral3" :foreground "white"))) "Face for the yasnippet selected candidate.")
(defvar ac-source-yasnippet '((candidates . ac-yasnippet-candidate) (action . yas/expand) (candidate-face . ac-yasnippet-candidate-face) (selection-face . ac-yasnippet-selection-face)) "Source for Yasnippet.")

上述代码中的candidate-face表示补全结果的颜色,而selection-face表示当你把光标移到这个补全结果上的时候的颜色,如下图所示:

在auto complete使用yasnippet引擎

在auto complete使用yasnippet引擎


上图中,defun和dired都是yasnippet引擎给出来的补全结果,其他的则是其他引擎的补全结果,yasnippet的补全结果用其他颜色标注,而当前所选的yasnippet引擎的补全候选则是用另外一种颜色标注,一目了然。
还有,比如你有个for的yasnippet片段定义,你输入fo的时候,在yasnippet中这时候你并不能按tab进行补全,但是在auto complete中,你加了yasnippet引擎后,auto complete会提示出yasnippet的补全结果for,就像下面这样:
在auto complete使用yasnippet引擎

在auto complete使用yasnippet引擎


这时候,你按回车的话,会自动扩展yasnippet的for片段的定义,变成下面这样:
Emacs的超级补全yasnippet

Emacs的超级补全yasnippet


那么auto complete是怎么做到不需要完整输入yasnippet的片段, 只需要输入部分就能补全yasnippet的片段呢? 很简单,大家看到上面代码中ac-source-yasnippet的定义就明白了,里面有一个(action . yas/expand),你应该猜到了吧,它的意思就是当你选中yasnippet的candidate并且按下回车的时候,auto complete会去执行那个yas/expand,而它正是yasnippet的补全命令。
经上所述,通过auto complete,你用yasnippet更方便了。

现在你该明白了,auto complete和yasnippet的区别了吧。

Nutch的简单使用

2009年11月25日 没有评论

Nutch是一个开源的搜索引擎,包括抓取,索引,搜索,不过它主要专注于抓取,下面我讲一下它的简单使用。

首先,从这里下载Nutch的最新release(作此文时最新release为1.0),或者从这里直接下载源码,然后解压。解压后,打开文件$NUTCH_HOME/conf/nutch-site.xml(NUTCH_HOME为你nutch所在的文件夹,这个nutch-site文件是nutch的配置文件,不要直接修改nutch-default文件,那个是nutch的默认配置,nutch-site.xml会覆盖nutch-default.xml中的配置,详情请见Nutch配置文件的加载。当然你也可以修改nutch-default,xml,但是nutch官方不推荐那样做),在<configuration>和</configuration>之间输入以下内容:

<property>
  <name>http.agent.name</name>
  <value>spider</value>
  <description>HTTP 'User-Agent' request header. MUST NOT be empty -
  please set this to a single word uniquely related to your organization.
NOTE: You should also check other related properties:
http.robots.agents http.agent.description http.agent.url http.agent.email http.agent.version
and set their values appropriately.
</description> </property>
<property> <name>http.robots.agents</name> <value>spider,*</value> <description>The agent strings we'll look for in robots.txt files, comma-separated, in decreasing order of precedence. You should put the value of http.agent.name as the first agent name, and keep the default * at the end of the list. E.g.: BlurflDev,Blurfl,* </description> </property>

其中字段“http.agent.name”为你的crawler的名字(记得早期的版本可以不填的,现在的版本不填就报错),字段http.robots.agents,也可以不填,但是不填的话抓取的时候nutch会报:

Fetcher: Your 'http.agent.name' value should be listed first in 'http.robots.agents' property.

烦的慌,你要是不怕烦的话可以不填。
然后再打开文件$NUTCH_HOME/conf/crawl-urlfilter.txt,把该文件里面的MY.DOMAIN.NAME替换成你想抓取的域名,比如apache.org。

修改完以上的配置,现在就可以抓取了,抓取之前你得建立一个文件,里面存放你要抓取的url,比如建立一个文件urls,内容为:http://lucene.apache.org/nutch/,把该文件放到目录urls下面,Nutch抓取的时候只能对一个目录下的所有文件中的url进行抓取,不能对一个文件中的url进行抓取(这是由它的分布式系统Hadoop的特性决定的)。抓取很简单:

$NUTCH_HOME/bin/nutch crawl urls -dir crawl -depth 2

urls为待抓取的urls目录,crawl为输出目录(可以不写,默认为"crawl-"加当前日期和时间),depth为抓取深度,默认为5。输出如下:

ahei@ubuntu3:~/nutch-1.0/bin$ ./nutch crawl urls -dir crawl -depth 2
crawl started in: crawl
rootUrlDir = urls
threads = 10
depth = 2
        Injector: starting
Injector: crawlDb: crawl/crawldb
Injector: urlDir: urls
Injector: Converting injected urls to crawl db entries.
Injector: Merging injected urls into crawl db.
Injector: done
Generator: Selecting best-scoring urls due for fetch.
Generator: starting
Generator: segment: crawl/segments/20091126170222
Generator: filtering: true
Generator: jobtracker is 'local', generating exactly one partition.
Generator: Partitioning selected urls by host, for politeness.
Generator: done.
Fetcher: Your 'http.agent.name' value should be listed first in 'http.robots.agents' property.
Fetcher: starting
Fetcher: segment: crawl/segments/20091126170222
Fetcher: threads: 10
QueueFeeder finished: total 1 records.
fetching http://lucene.apache.org/nutch/
-finishing thread FetcherThread, activeThreads=1
-finishing thread FetcherThread, activeThreads=1
-finishing thread FetcherThread, activeThreads=1
-finishing thread FetcherThread, activeThreads=1
-finishing thread FetcherThread, activeThreads=1
-finishing thread FetcherThread, activeThreads=1
-finishing thread FetcherThread, activeThreads=1
-finishing thread FetcherThread, activeThreads=1
-finishing thread FetcherThread, activeThreads=1
-activeThreads=1, spinWaiting=0, fetchQueues.totalSize=0
-activeThreads=1, spinWaiting=0, fetchQueues.totalSize=0
-finishing thread FetcherThread, activeThreads=0
-activeThreads=0, spinWaiting=0, fetchQueues.totalSize=0
-activeThreads=0
Fetcher: done
CrawlDb update: starting
CrawlDb update: db: crawl/crawldb
CrawlDb update: segments: [crawl/segments/20091126170222]
CrawlDb update: additions allowed: true
CrawlDb update: URL normalizing: true
CrawlDb update: URL filtering: true
CrawlDb update: Merging segment data into db.
CrawlDb update: done
Generator: Selecting best-scoring urls due for fetch.
Generator: starting
Generator: segment: crawl/segments/20091126170233
Generator: filtering: true
Generator: jobtracker is 'local', generating exactly one partition.
Generator: Partitioning selected urls by host, for politeness.
Generator: done.
Fetcher: Your 'http.agent.name' value should be listed first in 'http.robots.agents' property.
Fetcher: starting
Fetcher: segment: crawl/segments/20091126170233
Fetcher: threads: 10
QueueFeeder finished: total 38 records.
fetching http://wiki.apache.org/nutch/
fetching http://issues.apache.org/jira/browse/Nutch
fetching http://lucene.apache.org/nutch/tutorial.html
-activeThreads=10, spinWaiting=7, fetchQueues.totalSize=35
-activeThreads=10, spinWaiting=9, fetchQueues.totalSize=35
fetching http://lucene.apache.org/nutch/skin/breadcrumbs.js
-activeThreads=10, spinWaiting=9, fetchQueues.totalSize=34
Error parsing: http://lucene.apache.org/nutch/skin/breadcrumbs.js: org.apache.nutch.parse.ParseException: parser not found for contentType=application/javascript url=http://lucene.apache.org/nutch/skin/breadcrumbs.js
        at org.apache.nutch.parse.ParseUtil.parse(ParseUtil.java:74)
        at org.apache.nutch.fetcher.Fetcher$FetcherThread.output(Fetcher.java:766)
        at org.apache.nutch.fetcher.Fetcher$FetcherThread.run(Fetcher.java:552)
-activeThreads=10, spinWaiting=10, fetchQueues.totalSize=34 fetching http://lucene.apache.org/nutch/version_control.html -activeThreads=10, spinWaiting=9, fetchQueues.totalSize=33 -activeThreads=10, spinWaiting=10, fetchQueues.totalSize=33 fetching http://wiki.apache.org/nutch/FAQ fetching http://lucene.apache.org/nutch/apidocs-0.8.x/index.html -activeThreads=10, spinWaiting=8, fetchQueues.totalSize=31 -activeThreads=10, spinWaiting=9, fetchQueues.totalSize=31 fetching http://lucene.apache.org/hadoop/ -activeThreads=10, spinWaiting=8, fetchQueues.totalSize=30 -activeThreads=10, spinWaiting=10, fetchQueues.totalSize=30 fetching http://forrest.apache.org/ -activeThreads=10, spinWaiting=9, fetchQueues.totalSize=29 -activeThreads=10, spinWaiting=9, fetchQueues.totalSize=29 -activeThreads=10, spinWaiting=10, fetchQueues.totalSize=29 fetching http://lucene.apache.org/nutch/apidocs-0.9/index.html -activeThreads=10, spinWaiting=9, fetchQueues.totalSize=28 -activeThreads=10, spinWaiting=10, fetchQueues.totalSize=28 fetching http://lucene.apache.org/nutch/credits.html -activeThreads=10, spinWaiting=10, fetchQueues.totalSize=27 fetching http://www.apache.org/dist/lucene/nutch/CHANGES-0.9.txt -activeThreads=10, spinWaiting=9, fetchQueues.totalSize=26 -activeThreads=10, spinWaiting=9, fetchQueues.totalSize=26 -activeThreads=10, spinWaiting=9, fetchQueues.totalSize=26 -activeThreads=10, spinWaiting=10, fetchQueues.totalSize=26 -activeThreads=10, spinWaiting=10, fetchQueues.totalSize=26 -activeThreads=10, spinWaiting=10, fetchQueues.totalSize=26 -activeThreads=10, spinWaiting=10, fetchQueues.totalSize=26

抓取完数据之后怎样检验呢?使用命令:

$NUTCH_HOME/bin/nutch org.apache.nutch.searcher.NutchBean apache

这个命令会给出apache的搜索结果,这个命令默认是对crawl目录进行搜索,这是代码证明:

文件:$NUTCH_HOME/src/java/org/apache/nutch/searcher/NutchBean.java:87
  public NutchBean(Configuration conf, Path dir) throws IOException {
    this.conf = conf;
    this.fs = FileSystem.get(this.conf);
    if (dir == null) {
      dir = new Path(this.conf.get("searcher.dir", "crawl"));
    }

要想对其他目录进行搜索,在nutch-site.xml中加入以下内容:

<property>
  <name>searcher.dir</name>
  <value>other-searcher-dir</value>
  <description>
  Path to root of crawl.  This directory is searched (in
  order) for either the file search-servers.txt, containing a list of
  distributed search servers, or the directory "index" containing
  merged indexes, or the directory "segments" containing segment
  indexes.
  </description>
</property>

搜索结果如下:

ahei@ubuntu3:~/nutch-1.0/bin$ ./nutch org.apache.nutch.searcher.NutchBean apache
Total hits: 25
0 20091126170222/http://lucene.apache.org/nutch/
... Lucene. January 2005: Nutch Joins Apache Incubator Nutch is a ... determined that the Apache license is the appropriate
1 20091126170233/http://www.apache.org/
... including Apache XML, Apache Jakarta, Apache Cocoon, Apache Xerces, Apache Ant, and Apache ... Source projects such as NoSQL, Apache ...
2 20091126170233/http://www.apache.org/licenses/
... Copyright © 2009 The Apache Software Foundation, Licensed under the ... Apache License, Version 2.0 . Apache ... Apache and the ...
3 20091126170233/http://forrest.apache.org/
... Welcome to Apache Forrest apache > forrest   Welcome Developers Versioned Docs ... Example sites Thanks Related projects Apache Gump Apache ...
4 20091126170233/http://lucene.apache.org/
... the release of Apache Mahout 0.1. Apache Mahout is a subproject ... on top of ...
5 20091126170233/http://wiki.apache.org/nutch/
FrontPage - Nutch Wiki Search: Nutch Wiki Login FrontPage FrontPage RecentChanges FindPage HelpContents Immutable Page Comments Info Attachments More Actions: ...
6 20091126170233/http://lucene.apache.org/nutch/index.html
... Lucene. January 2005: Nutch Joins Apache Incubator Nutch is a ... determined that the Apache license is the appropriate
7 20091126170233/http://wiki.apache.org/nutch/FAQ
... all available at http://lucene.apache.org/nutch/mailing_lists.html . How ...
8 20091126170233/http://lucene.apache.org/nutch/tutorial8.html
... http://([a-z0-9]*.)*apache.org/ This will include any ... in the domain apache.org . Edit the file ...
9 20091126170233/http://lucene.apache.org/nutch/tutorial.html
... crawl to the apache.org domain, the line ... http://([a-z0-9]*.)*apache.org/ This will include any

Nutch的入门使用很简单吧,上面所述只是在一台机器上进行抓取,Nutch有个分布式系统Hadoop,可以实现分布式抓取,后续的文章中,我会讲下怎样实现Nutch的分布式抓取,敬请期待, :)

Emacs才是世界上最强的IDE - cedet的安装

2009年11月24日 没有评论

在准备写Emacs才是世界上最强大的IDE系列文章时,由于以前用的是cedet1.0pre6版本,速度比较慢,而且精确度也不高,所以就没有打算写它,昨天晚上在水木群里听一位网友介绍说cedet的cvs版本速度很快,而且精确度不错,所以晚上就down下cvs的版本试了一把,越试越感觉不错,就有了写cedet的想法。从头开始,先说安装吧。

cedet的安装挺简单,只要仔细看好cedet的说明就可以了,不过我昨天晚上没仔细看好说明,安装的时候碰到一个问题,所以我还是准备写一下安装过程。

首先下载cedet的cvs版本:

cvs -d:pserver:anonymous@cedet.cvs.sourceforge.net:/cvsroot/cedet login
cvs -z3 -d:pserver:anonymous@cedet.cvs.sourceforge.net:/cvsroot/cedet co -P cedet

然后编译:

  • linux下:
    cd cedet
    touch `find . -name Makefile` (非cvs版本不需要这个)
    a) make
    或
    b) make EMACS=
    或
    c) make MAKEINFO=/usr/local/bin/makeinfo
    或
    d) make MAKEINFO=echo
    如果编译过程中发生类似找不到loaddef文件,custom-autoload以及其他奇怪的错误,执行:
    a) make clean-autoloads
    b) make clean-all
    然后再重新编译。编译错误还有可能是make版本引起的,请使用GNU make,或者使用windows下的安装方法。
    
  • windows下(当然linux下这样也可以):
    emacs -q --no-site-file -l cedet-build.el -f cedet-build
    或
    在emacs中打开cedet-build.el文件,然后
    M-x eval-buffer
    M-x cedet-build-in-default-emacs
    编译过程中如果发生超过emacs堆栈大小的错误,退出emacs再重新编译即可。
    

说完安装,再说一下简单的使用,把下面的代码贴到你的.emacs里面:

(add-to-list 'load-path "~/cedet")
(require 'cedet)
(require 'semantic-ia)
;; Enable EDE (Project Management) features (global-ede-mode 1)
(semantic-load-enable-excessive-code-helpers) (semantic-load-enable-semantic-debugging-helpers)
;; Enable SRecode (Template management) minor-mode. (global-srecode-minor-mode 1)

现在开始享受cedet吧。

关于cedet的进一步使用,我会再写专门的文章介绍,敬请期待, :)