博客搬家
yo2是我真正使用的第一个博客, 我挺喜欢的, 可是自从去年yo2被关闭后, 我不得不重新考虑新的博客, 最终自己决定租了个虚拟空间, 买了个域名. 关于Emacs方面的内容, 可以访问Emacs中文网, 这个网站是我专门用于和大家一起探讨Emacs使用心得的, 关于其他方面的技术文章, 可以访问我的个人博客. 最后谢谢郭嘉, 谢谢yo2, 谢谢大家.
yo2是我真正使用的第一个博客, 我挺喜欢的, 可是自从去年yo2被关闭后, 我不得不重新考虑新的博客, 最终自己决定租了个虚拟空间, 买了个域名. 关于Emacs方面的内容, 可以访问Emacs中文网, 这个网站是我专门用于和大家一起探讨Emacs使用心得的, 关于其他方面的技术文章, 可以访问我的个人博客. 最后谢谢郭嘉, 谢谢yo2, 谢谢大家.
好久没去上水木了, 今天上去瞧瞧. 本来以为几天不去了, 会有好多帖子, 上去之后, 只有一点帖子, 有点失望, 呵呵.
废话少说, 看到一个网友的问题:
求帮看看这句话为什么老有错误信息
(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:3753f @code{post-command-hook}. ./doc/emacs-w3m.texi:3699
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) ""))
就可以了. 很简单吧. ![]()
前阵子, 有朋友建议我注册个独立域名, 买个空间, 那样会稳定些. 昨天同事说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态度不错, 不过有点像机器人,
.
你有没有遇到过这种情况: 某一台机器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的配置文件主要有三类:
这些配置文件的加载顺序决定了它们的优先级,优先级低的会被优先级高的配置文件中的配置覆盖,所以要想配置好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配置文件的优先级为:
需要专门花时间去学的软件为数不多, 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的话, 不要再找借口了. 一失足成千古恨, 再回首已百年身啊. 趁年轻喜欢折腾, 多折腾折腾, 不要等到老来少年之狂已成往事之时, 追悔莫及啊. 我以前的一个同事,他以前的公司同事都用vim,所以也用vim,自从我向他介绍了Emacs后,开始学Emacs,一开始也没觉得好用,但是他坚持下来了,越用越好用,最终觉得Emacs太好用了。他是第一个我传道Emacs成功的对象,
。
当你掌握了上述的基本概念和基础知识后, 恭喜你, 你将开始初学者的第二个阶段.
接下来, 你可以去网上找一些牛人的配置来看看, 比如王垠(清华的退学博士, 很佩服), 叶文斌(他的主页已经不在了, 我备份了一份, 可惜yo2的上传文件竟然不能超过2M,。。。,有需要的同志到关于页面找联系方式致电我), 王纯业(他的主页也不在了), pluskid(浙大的牛人, yasnippet的作者). 我刚开始学Emacs的时候, 基本上也是看他们的配置. 看到别人的配置中比较有意思的地方, 你再拷贝到你的配置中, 这样看多了, 你就慢慢熟悉Emacs了. 但是我建议不要直接拿别人的配置文件来使用, 毕竟别人的配置是别人的, 它包括了别人的使用习惯, 不一定适合你. 我的配置中就改了一些Emacs基本的快捷键, 比如`C-k'我改成了删除一行, 而不是删除光标到行尾, 这也许不适合你. 当然我对于那些尽量能提供给别人的配置我是单独拿出来做成一个包的, 比如我的颜色主题color-theme-ahei, 还有dired-lis(这是一个使得你在dired中直接输入字母跳到对应文件的包, 就像Total Commander中那样, 非常的好用). 你可以去直接使用这些包, 再经过你自己的配置, 就为你所用了.
初学者在配置Emacs的过程中, 我相信经常会遇到加了某个配置后, 以前某个能用的功能现在不能用了, 不用着急, 我有三条妙计为你排忧解难:
什么是二分调试法? 名字很酷吧? 我自己取的,
二分调试法首先本质上也是排除法, 其次, 二分嘛, 肯定与二分搜索有关系嘛, 对头, 它就是利用了二分搜索法的算法思想. 假如对于64行的配置文件, 现在出现某个问题了, 我首先把后面32行注释掉(用(when nil)注释, 方便快捷), 看看有没有问题:
经过以上递归判断, 最终肯定能找到问题所在. 好多问题我都是靠这种方法解决的.
二分二分嘛, 当然也和二分搜索法效率一样的了, 复杂度lgN, 所以甭管多大的配置文件, 二分调试法一会就能帮你找到问题所在. 100万行?哇,好大,20次搞定!算法的威力强大吧?!
当然适用
你不会傻的真的完全二分吧? 二分的时候对于代码块, 比如for, while等要放在一起, 不能分开.
经过上述的锻炼, 我相信你现在应该可以熟练的使用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包吧!
Eclipse是一个比较不错的IDE(当然与Emacs比起来还是差不少,
), 现在用它的人挺多的, 用过的用户应该都会知道, 在Eclipse里面, 当把光标移到一个变量, 或者方法, 或者类名, 或者类型名上面的时候, Eclipse会把这个符号的所在的作用域中的所有这个符号都高亮, 比如局部变量abc, 它就会把当前作用域内所有的局部变量, 就像下面这个样子:
但是它并不会高亮与它同名的但是不同类型的单词, 比如它并不会高亮字符串"abc", 如上图所示. 它也不会高亮和它不在同一作用域内的单词, 比如上图中的下面那个大括号扩住的代码块内的abc, 虽然和上面的代码块的abc同类型, 但是不是同一作用域, Eclipse也不会高亮它, 甚至, Eclipse都不会高亮当前光标处的单词的重载方法(这点感觉不太方便), 如下图所示:
总之, Eclipse只会高亮当前作用域内和当前光标处单词完全是同一个语法个体的单词.
那么Emacs中是否有类似的功能呢, 当然有! highlight-symbol这个包就我们实现了这个功能, 我们先来瞧瞧它长啥样:
上图中那些灰色背景, 蓝色前景的"defconst"就是用highlight-symbol高亮的,漂亮吧,
.
highlight-symbol与Eclipse的高亮功能有哪些不同点呢?
总之, 有优点也有缺点, 不过优点明显多于缺点.
我觉得一个好的高亮当前光标处单词的工具应该把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是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的区别了吧。
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系列文章时,由于以前用的是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
然后编译:
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下的安装方法。
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的进一步使用,我会再写专门的文章介绍,敬请期待,
。