2009年4月5日星期日

我的vim技巧集

平常用到的vim技巧,整理记录在blog上,方便查找。

VIM中,移动光标到下一个单词的词首,使用命令"w",移动光标到上一个单词的词首,使用命令"b";移动光标到下一个单词的结尾,用命令"e",移动光标到上一个单词的结尾,使用命令"ge"。

上面这些命令都使用'iskeyword'选项中的字符来确定单词的分界,还有几个命令,只把空白字符当做"单词"的分界。当然,这里说的"单词"已经不是传统意义上的单词了,而是由非空白字符构成一串字串。命令"W"移动光标到下个字串的开始,命令"B"移动到上个字串的开始;命令"E"移动到下个字串的结尾,命令"gE"移动到上个字串的结尾。

使用H/M/L这三个键,可以让光标跳到当前窗口的顶部、中间、和底部,停留在第一个非空字符上。H命令和L命令前也可以加一个数字,但数字的含义不再是倍数,而是指距窗口顶部、底部的行数。例如,"3H"表示光标移动到距窗口顶部第3行的位置;"5L"表示光标移动到距窗口底部5行的位置

在阅读代码时,有时我们需要根据光标所在的位置滚屏,把光标所在行移动窗口的顶端、中间或底部,这时就可以用到"zt"、"zz"和"zb"。这种滚屏方式相对于翻页来讲,它的好处在于,你能够始终以当前光标位置做为参照,不会出现翻几次页后,发现自己迷失了方向。
^_^
********************************************************************************

1.
选取多个文件用vim打开,只用一个窗口,文件都列在buffer中(这个在windows找到办法了,我用的TC+F4menu,在F4menu设置中的调用vim的"打开方式"项选择"所有文件以列表方式打开",就会将选取的文件都读进buffer,但窗口只显示一个),
gvim --remote-tab-silent
如果在命令行中,直接加参数-p即可在多tab页中分别打开文件,如:vim -p
file1.c file2.c

2. vim用什么方法显示16进制?
:%!xxd

3. 怎样在vim中粘贴进桌面剪贴板里的内容?
比如我在opera里复制的内容粘贴到vim里.
"*p
"+p

4. 在文本wrap的时候怎样才能跳到上一行而不是上一段? 如同notepad里那样?
加个g,如:gj,gk.....
d:\soft

5. 请问vim在命令行上可以复制粘贴么?
Ctrl+r"或者Ctrl+r Ctrl+w可以粘贴到命令行 "
而对把编辑区到命令行,可以先拷贝到如a寄存器,然后在命令行ctrl-r a

6. 如何实现行之间的倒序排序
比如
a
c
b
变成
b
c
a
答案::g/./m0
解释:
:h :m
m->move <address>
m0->把匹配行移动到第一行之前

这是VIM帮助文档里一个tip
:h 12.4

7.怎么实现文本的自然排序。
比如
d
a
c
b
排成
a
b
c
d
答案::sort

8. 如何删除含有某些内容的行?
例如,想要删除含有console的行,怎么写呢?
:g/console/d

9.搜索到一行如何删除该行上面的5行?
:g/string/norm d5k
:g/搜索内容/normal d5k

10. 请问怎样每隔x行插入一个空行
答:
假设每隔5行吧,
qa4jo<Esc>jq
然后 @a就可以了
或者:
:%s/\(.*\n\)\{3}/\0\r/g
3换成你要的数字

11. 如何用vim的列操作加注释
用c-v选中了一列,输入大写I或者大写A,然后输入comment符号,然后esc

12.操作多个文件其实超级简单
o newfilename
:bn buffer Next
:bp Buffer Pervious
:bN The list number N of the buffers
:sp newfile // open a newfile with a splited window
:vs newfile // open a newfile with a vertical splited window
[ctrl] +j // down window (same with the key j)
[ctrl] +k // up window (same with the key k)
[ctrl] +h // left window (same with the key h)
[ctrl] +l // right window (same with the key l)

13.一组能让你爽出内伤的 Vim motion
用vim这么长时间,当看到下边几句,对vim的认识提高一个台阶。
ci[ 删除一对 [] 中的所有字符并进入插入模式
ci( 删除一对 () 中的所有字符并进入插入模式
ci< 删除一对 <> 中的所有字符并进入插入模式
ci{ 删除一对 {} 中的所有字符并进入插入模式
cit 删除一对 HTML/XML 的标签内部的所有字符并进入插入模式
ci" ci' ci` 删除一对引号字符 (" 或 ' 或 `) 中所有字符并进入插入模式

14. 统计关键字的个数
:%s/pattern/&/g
&代表的意思就是用来表示前面比对的字串,所以做这个指令其实对档案本身并不会有什么改变。但是由於做的是全域的取代置换,vim会告诉你有从多少行中多少个字串被取代。轻轻松松很漂亮地用一行命令解决这个问题。
输入只有一行的命令:结果就会出现在最后一行上,而且不会改变到档案本身.

15. 查找关键字
可按『*』来选取关键字。不过 vim 在这里搜寻边界是根据空白或者像「 { 」、「 (
」这些符号等来决定,所以对於中文来说,这个功能可能就不是那么好用。可以用『v』选取然后『y』复制。搜索时,在『/』后,按<
Ctrl-R >然后跟上数字键 0 ,就会出现刚刚复制进入register的字串。指令如下:
/<Ctrl-R>0<CR>
在 vim的register中,register 0是作删除或是 yank 动作时,预设使用的暂存器。
不过在前面可以发现,如果我们用「 * 」作搜寻的时候,字串的前后会被「 \< \>
」夹著。举例来说,如果你找的是 \<link\>,那么, min_links
就不会符合,同样,linknode也不会符合。这也就是说「 \<
」表示的是一个字的头,而 「 \>
」代表的是一个字的尾,当你写\<link的时候,表示你要找的字,是以link开头的,同理,link\>就是以link结尾的字,所以用两个夹起来,就代表要字串的的确确是所要的那个字。

而既然有了 「*」往下找的,就有往回找的,按键是「 # 」
,你懒得记那么多的话,就用 「*」 然后搭配 「 N 」 吧!

到这里,你可能会想,如果我不想打那么多字,可是我想要的又不要完全是一样的,怎么办?
vim 也有这样的东西,就是在你打「*」之前,先加一个 「g」,也就是
g*
这样 vim 在做搜寻的时候,就不会包含 \< \> 这两个东西在前后了。同样「 #
」的使用也可以变成「 g# 」

16. 查看符号定义
Vi提供了像Win32下IDE那样的功能,可以很方便的查看函数原型、结构声明、宏的定义等。只是它的功能相比之下,要弱一些,不过,结合其它一些技巧,完全可以满足你的需要。使用这个功能,需要做一些设置:
1.
安装ctags软件包。在安装光盘可以找到,也可以到网上下载源码包,自己编译。
2. 生成tags文件。进入到你的源代码所在的目录,运行ctags
-R命令,它会为当前目录及子目录下的源程序建立索引,并在当前目录下创建一个tags文件,里面保存的是符号索引信息。
3.
设置tags路径。在vi的起动脚本文件中(一般是~/.vimrc),告诉vi在哪里可以找到tags文件,一般尽量用相对路径。如:set
tags=./tags,../tags,../../tags,../../../tags
4. 跳到指定的符号。<ctrl> + ]
可以跳转到光标所在处的符号的符号的定义那里。
5. 返回到原来的位置。<ctrl> + t 或者<ctrl> + o可以返回原来的位置。

17. 查看系统函数的帮助
Vi也可以像VC那样,很方便的跳转到系统函数的帮助那里。原理很简单,在vi运行外部命令man就行了:!man
fopen,这样做,可能会觉得有些麻烦,vi提供了快捷的方式: <shift> +
k可以跳到光标所在处的符号的帮助那里。

18. 自动定位编译错误处
在VC里,编译时,如果出现编译错误,双击错误信息,编辑器自动切换到出现错误的地方,是不是很方便呢?其实不用羡慕,vi也有这种功能,在vi里,运行make命令后,如果有编译错误,你按一下回车,vi自动定位到第一个编译错误那里。记得,要用内置的make命令,即运行:make,不是外部命令:!make。

19.自动定位查找结果
在写程序时,我们常常想知道,有哪些地方使用了某个函数,怎么办呢,你可以在shell里,用grep查找,然后打开对应的文件,可以看到相关的上下文信息。但这样做比较麻烦,Vi有个内置的grep命令,用起来很方便:
1. 查找。用法和shell中的grep一致。
2. 跳到第一个查找结果处。直接回车就行了。
3.
列出所有的查找结果。:cl命令可以列出所有的查找结果,每个结果都有一个编号。
4. 跳到某项查找结果的文件中。:cc <编号>
命令可以跳到指定编号查找结果的文件中,<编号>是前面用:cc列出来的编号。
5. 返回到原来的位置。<ctrl> + o 可以返回原来的位置。

Makefile好助手:pkgconfig

你在Unix下开发过软件吗?写完一个程序,编译运行完全正常,在你本机上工作得好好的,你放到源代码管理系统中。然后,告诉你的同事说,你可以取下来用了。这时,你长长的出了一口气,几天的工作没有白费,多么清新的空气啊,你开始飘飘然了。

"Hi,怎么编译不过去?"你还沉浸在那种美妙的感觉之中,双臂充满着力量,似乎没有什么问题能难倒你的。正在此时,那个笨蛋已经冲着你嚷开了。

"不会吧,我这边好好的!"表面上你说得很客气,其实,你心里已经骂开了,真笨,不知道脑子干嘛用的。也许,你想的没错,上次,他犯了一个简单的错误,不是你一去就解决了吗。

他喊三次之后,你不得不放下你手上的工作,刚才那种美妙的感觉已经消失得无影无踪了,要不是你把情绪控制得很好,一肚子气就要撒在他身上了。你走到他的电脑前,键入make,优雅的按下回车。怎么可能出错呢?你信心十足。然而,屏幕上的结果多少有点让人脸红,该死的,libxxx.so怎么会让不到呢?

你在/usr目录中查找libxxx.so,一切都逃不过你的眼睛。奇怪,libxxx.so怎么在/usr/local/lib下,不是应该在/usr/lib下的吗?这你可不能怪别人,别人想安装在哪里都行,下次还可能安装到/lib目录下呢。

以上的场景并非虚构,我都经历过好几次,明明在本机上好好的,在别人的机器上连编译都过不去。可能两人的操作系统一模一样,需要的库都安装上,只是由于个人喜好不同,安装在不同的目录而已。遇到这种情况,每次都技巧性的绕过去了,用的补丁型的方法,心里老惦记其它地方能不能工作。

今天我们要介绍的pkgconfig,为解决以上问题提供了一个优美方案。从此,你再也不为此担忧了。Pkgconfig提供了下面几个功能:

1.
检查库的版本号。如果所需要的库的版本不满足要求,它会打印出错误信息,避免链接错误版本的库文件。
2. 获得编译预处理参数,如宏定义,头文件的位置。
3.
获得链接参数,如库及依赖的其它库的位置,文件名及其它一些连接参数。
4. 自动加入所依赖的其它库的设置。

这一切都自动的,库文件安装在哪里都没关系!

在使用前,我们说说pkgconfig的原理,pkgconfig并非精灵,可以凭空得到以上信息。事实上,为了让pkgconfig可以得到这些信息,要求库的提供者,提供一个.pc文件。比如gtk+-2.0的pc文件内容如下:

prefix=/usr
exec_prefix=/usr
libdir=/usr/lib
includedir=/usr/include
target=x11

gtk_binary_version=2.4.0
gtk_host=i386-redhat-linux-gnu

Name: GTK+
Description: GIMP Tool Kit (${target} target)
Version: 2.6.7
Requires: gdk-${target}-2.0 atk
Libs: -L${libdir} -lgtk-${target}-2.0
Cflags: -I${includedir}/gtk-2.0

这个文件一般放在/usr/lib/pkgconfig/或者/usr/local/lib/pkgconfig/里,当然也可以放在其它任何地方,如像X11相关的pc文件是放在/usr/X11R6/lib/pkgconfig下的。为了让pkgconfig可以找到你的pc文件,你要把pc文件所在的路径,设置在环境变量PKG_CONFIG_PATH里。

使用方法很简单,比如,我们要使用gtk+的库编译一个程序:
gcc -g arrow.c -o arrow `pkg-config "gtk+-2.0 > 2.0.0" --cflags --libs`

只要安装了gtk+2.0,不管它在哪里,编译都是正常的。这是不是简单很多了?

Linux下的调试工具

随着XP的流行,人们越来越注重软件的前期设计、后期的实现,以及贯穿于其中的测试工作,经过这个过程出来的自然是高质量的软件。甚至有人声称XP会淘汰调试器!这当然是有一定道理的,然而就目前的现实来看,这还是一种理想。在日常工作中,调试工具还是必不可少的。在Linux下,调试工具并非只有gdb,还有很多其它调试工具,它们都各有所长,侧重方面也有所不同。本文介绍几种笔者常用的调试工具:

1. mtrace
在linux下开发应用程序,用C/C++语言的居多。内存泄露和内存越界等内存错误,无疑是其中最头疼的问题之一。glibc为解决内存错误提供了两种方案:

一种是hook内存管理函数。hook内存管理函数后,你可以通过记下内存分配的历史记录,在程序终止时查看是否有内存泄露,这样就可以找出内存泄露的地方了。你也可以通过在所分配内存的首尾写入特殊的标志,在释放内存时检查该标志是否被破坏了,这样就可以达到检查内存越界问题的目的。

另外一种方法更简单,glibc已经为第一种方案提供了默认的实现,你要做的只是在特定的位置调用mtrace/muntrace两个函数,它们的函数原型如下:
#include <mcheck.h>
void mtrace(void);
void muntrace(void);
你可能会问,在哪里调这两种函数最好?这没有固定的答案,要视具体情况而定。对于小程序来说,在进入main时调用mtrace,在退出main函数时调用muntrace。对于大型软件,这样做可能会记录过多的信息,分析这些记录会比较慢,这时可以在你所怀疑代码的两端调用。

另外,还需要设置一个环境变量MALLOC_TRACE,它是一个文件名,要保证当前用户有权限创建和写入该文件。glibc的内存管理器会把内存分配的历史信息写入到MALLOC_TRACE指定的文件中。

程序运行完毕后,使用mtrace工具分析这些内存分配历史信息,可以查出内存错误的位置(mtrace在glibc-utils软件包里)。

2. strace
在编程时,检查函数的返回值是一种好习惯。对于像glibc等标准C的函数,光检查返回值是不够的,还需要检查errno的值。这样的程序往往显得冗长,不够简洁。同时也可能是出于偷懒的原因,大多数程序里并没有做这样的检查。

这样的程序,一旦出现错误,用调试器一步一步定位错误,然后想法查出错误的原因,也是可以的,不过比较麻烦,对调试器来说有些大材小用,不太可取。这时,用strace命令可能会更方便一点。它可以显示各个系统调用/信号的执行过程和结果。比如文件打开出错,一眼就看出来了,连错误的原因(errno)都知道。

3. binutil
binutil是一系列的工具,你可能根本不知道它们的存在,但是没有它们你却寸步难行。Binutil包括下列工具:
ld - the GNU linker.
as - the GNU assembler.
addr2line - Converts addresses into filenames and line numbers.
ar - A utility for creating, modifying and extracting from archives.
c++filt - Filter to demangle encoded C++ symbols.
gprof - Displays profiling information.
nlmconv - Converts object code into an NLM.
nm - Lists symbols from object files.
objcopy - Copys and translates object files.
objdump - Displays information from object files.
ranlib - Generates an index to the contents of an archive.
readelf - Displays information from any ELF format object file.
size - Lists the section sizes of an object or archive file.
strings - Lists printable strings from files.
strip - Discards symbols.
windres - A compiler for Windows resource files.
其中部分工具对调试极有帮助,如:
你可以用objdump反汇编,查看目标文件或可执行文件内部信息。
你可以用addr2line把机器地址转换到代码对应的位置。
你可以用nm查看目标文件或可执行文件中的各种符号。
你可以用gprof分析各个函数的使用情况,找出性能的瓶颈所在(这需要加编译选项)。

4. ld-linux
现在加载ELF可执行文件的工作,已经落到ld-linux.so.2头上了。你可能会问,这与有调试程序有关系吗?有的。比如,在linux中,共享库里所有非static的函数/全局变量都是export的,更糟的是C语言中没有名字空间这个概念,导致函数名极易冲突。在多个共享库中,名字冲突引起的BUG是比较难查的。这时,你可以通过设置LD_
DEBUG环境变量,来观察ld-linux.so加载可执行文件的过程,从中可以得到不少帮助信息。LD_
DEBUG的取值如下:
libs display library search paths
reloc display relocation processing
files display progress for input file
symbols display symbol table processing
bindings display information about symbol binding
versions display version dependencies
all all previous options combined
statistics display relocation statistics
unused determined unused DSOs
help display this help message and exit
5. gdb
对于真正意义的调试器来说,gdb在linux下是独一无二的。它有多种包装,有字符界面的,也有图形界面的,有单独运行的,也有集成到IDE中的。gdb功能强大,图形界面的gdb容易上手一点,但功能无疑受到了一些限制,相信大部分高手还是愿意使用字符界面的。Gdb太常用了,这里不再多说。

6. gcc/boundschecker
相信很多人用过win32下的BoundsChecker(Compuware公司)和Purify(IBM公司)两个工具吧。它们的功能实在太强大了,绝非能通过重载内存管理函数就可以做到,它们在编译时插入了自己的调试代码。

gcc也有个扩展,通过在编译时插入调试代码,来实现更强大的检查功能。当然这要求重新编译gcc,你可以到http://sourceforge.net/projects/boundschecking/
下载gcc的补丁。它的可移植性非常好,笔者曾一个ARM
平台项目里使用过,效果不错。

7. valgrind
最好的东西往往最后才见到。Valgrind是我的最爱,用习惯了,写的程序不在valgrind下跑一遍,就像没有写单元测试程序一样,有点放心不下。它有BoundsChecker/Purify的功能,而且速度更快。
有点遗憾的是valgrind目前只支持x86平台,当然,这对大多数情况已经足够了。
你可以到http://valgrind.org/ 下载最新版本。

Linux下共享库(SO)有关的几个环境变量

Linux支持共享库已经有悠久的历史了,不再是什么新概念了。大家都知道如何编译、连接以及动态加载(dlopen/dlsym/dlclose)
共享库。但是,可能很多人,甚至包括一些高手,对共享库相关的一些环境变量认识模糊。当然,不知道这些环境变量,也可以用共享库,但是,若知道它们,可能就会用得更好。下面介绍一些常用的环境变量,希望对家有所帮助:

LD_LIBRARY_PATH
这个环境变量是大家最为熟悉的,它告诉loader:在哪些目录中可以找到共享库。可以设置多个搜索目录,这些目录之间用冒号分隔开。在linux下,还提供了另外一种方式来完成同样的功能,你可以把这些目录加到/etc/ld.so.conf中,或则在/etc/ld.so.conf.d里创建一个文件,把目录加到这个文件里。当然,这是系统范围内全局有效的,而环境变量只对当前shell有效。按照惯例,除非你用上述方式指明,loader是不会在当前目录下去找共享库的,正如shell不会在当前目前找可执行文件一样。

LD_PRELOAD
这个环境变量对于程序员来说,也是特别有用的。它告诉loader:在解析函数地址时,优先使用LD_PRELOAD里指定的共享库中的函数。这为调试提供了方便,比如,对于C/C++程序来说,内存错误最难解决了。常见的做法就是重载malloc系列函数,但那样做要求重新编译程序,比较麻烦。使用LD_PRELOAD机制,就不用重新编译了,把包装函数库编译成共享库,并在LD_PRELOAD加入该共享库的名称,这些包装函数就会自动被调用了。在linux下,还提供了另外一种方式来完成同样的功能,你可以把要优先加载的共享库的文件名写在/etc/ld.so.preload里。当然,这是系统范围内全局有效的,而环境变量只对当前shell有效。

LD_ DEBUG
这个环境变量比较好玩,有时使用它,可以帮助你查找出一些共享库的疑难杂症(比如同名函数引起的问题)。同时,利用它,你也可以学到一些共享库加载过程的知识。它的参数如下:
libs display library search paths
reloc display relocation processing
files display progress for input file
symbols display symbol table processing
bindings display information about symbol binding
versions display version dependencies
all all previous options combined
statistics display relocation statistics
unused determined unused DSOs
help display this help message and exit
BIND_NOW
这个环境变量与dlopen中的flag的意义是一致,只是dlopen中的flag适用于显示加载的情况,而BIND_NOW/BIND_NOT适用于隐式加载。

LD_PROFILE/LD_PROFILE_OUTPUT:为指定的共享库产生profile数据,LD_PROFILE指定共享库的名称,LD_PROFILE_OUTPUT指定输出profile文件的位置,是一个目录,且必须存在,默认的目录为/var/tmp/或/var/profile。通过profile数据,你可以得到一些该共享库中函数的使用统计信息。
Linux下共享库(SO)有关的几个环境变量收藏
Linux支持共享库已经有悠久的历史了,不再是什么新概念了。大家都知道如何编译、连接以及动态加载(dlopen/dlsym/dlclose)
共享库。但是,可能很多人,甚至包括一些高手,对共享库相关的一些环境变量认识模糊。当然,不知道这些环境变量,也可以用共享库,但是,若知道它们,可能就会用得更好。下面介绍一些常用的环境变量,希望对家有所帮助:

LD_LIBRARY_PATH
这个环境变量是大家最为熟悉的,它告诉loader:在哪些目录中可以找到共享库。可以设置多个搜索目录,这些目录之间用冒号分隔开。在linux下,还提供了另外一种方式来完成同样的功能,你可以把这些目录加到/etc/ld.so.conf中,或则在/etc/ld.so.conf.d里创建一个文件,把目录加到这个文件里。当然,这是系统范围内全局有效的,而环境变量只对当前shell有效。按照惯例,除非你用上述方式指明,loader是不会在当前目录下去找共享库的,正如shell不会在当前目前找可执行文件一样。

LD_PRELOAD
这个环境变量对于程序员来说,也是特别有用的。它告诉loader:在解析函数地址时,优先使用LD_PRELOAD里指定的共享库中的函数。这为调试提供了方便,比如,对于C/C++程序来说,内存错误最难解决了。常见的做法就是重载malloc系列函数,但那样做要求重新编译程序,比较麻烦。使用LD_PRELOAD机制,就不用重新编译了,把包装函数库编译成共享库,并在LD_PRELOAD加入该共享库的名称,这些包装函数就会自动被调用了。在linux下,还提供了另外一种方式来完成同样的功能,你可以把要优先加载的共享库的文件名写在/etc/ld.so.preload里。当然,这是系统范围内全局有效的,而环境变量只对当前shell有效。

LD_ DEBUG
这个环境变量比较好玩,有时使用它,可以帮助你查找出一些共享库的疑难杂症(比如同名函数引起的问题)。同时,利用它,你也可以学到一些共享库加载过程的知识。它的参数如下:
libs display library search paths
reloc display relocation processing
files display progress for input file
symbols display symbol table processing
bindings display information about symbol binding
versions display version dependencies
all all previous options combined
statistics display relocation statistics
unused determined unused DSOs
help display this help message and exit
BIND_NOW
这个环境变量与dlopen中的flag的意义是一致,只是dlopen中的flag适用于显示加载的情况,而BIND_NOW/BIND_NOT适用于隐式加载。

LD_PROFILE/LD_PROFILE_OUTPUT:为指定的共享库产生profile数据,LD_PROFILE指定共享库的名称,LD_PROFILE_OUTPUT指定输出profile文件的位置,是一个目录,且必须存在,默认的目录为/var/tmp/或/var/profile。通过profile数据,你可以得到一些该共享库中函数的使用统计信息。

Linux下文件关联的实现原理

From:http://blog.csdn.net/absurd/

我们知道在Windows下,双击一个可执行文件,文件管理器会自动运行这个应用程序。而双击一个数据文件时,文件管理器会用与之关联的应用程序打开它。数据文件与应用程序之间的关联是通过注册表来实现的:文件管理器查询注册表,找到数据文件对应的应用程序,然后运行这个应用程序,并把数据文件的文件名作为命令行参数传给它。

这种文件关联的方式非常好用,省去了先起动应用程序再打开文件的麻烦。Linux下的桌面环境也有类似的功能,而且实现方式更合理。最近负责开发一个资源管理器,本来GNOME有一个功能强大的资源管理器Nautilus,只是它过于庞大,不但有超过10万行的代码,还依赖于libgnome、gnome-vfs和CORBA等,故不适合于嵌入式应用。最终我们决定自己开发一个简化的资源管理器,但又要尽量兼容现有的应用程序,这要了解相关标准,文件关联方式是其中之一。把这几天学到的知识做个笔记吧,供有兴趣的朋友参考:

首先让我们看看文件关联要做些什么。
1.
数据文件与应用程序的关联。一个应用程序通常只能打开一些特定的数据文件,比如图片浏览工具可以打开PNG、BMP和JPEG等图片文件。打开一词的意义比较宽泛,这里包括:打开、播放、安装、编辑和打印等等。

2.
文件类型信息。资源管理器把数据文件列出来时,通常会用一个图标来标识这类文件,同时也会加上一个简短的名称,以便用户可以很容易把它与其它类型的文件区分开来。

下面我们看看linux下是如何实现的。
1.
判断文件类型。文件的数量是无限的,我们只能按文件类型来处理。如何判断一个文件所属的文件类型呢?可能有人会说,很简单,用扩展名区分就行了。没错,用扩展名可以做到,但这种方法有两个缺陷:一方面它不是很精确,相同扩展名的文件的类型可能完全不同,比如dat文件,可能是一个视频文件,也可能是一个普通数据文件。另一方面它不是很准确,扩展名可以任何改动,为了某种目的,完全可以把exe扩展名改为htm扩展名。

而且在Linux下扩展名只是一个可选项,很多文件根本没有扩展名,所以纯粹采用文件扩展名的方式来判断肯定是不行的。为了更好的判断文件类型,在linux下同时采用两种方式:优先采用magic方式,其次才采用文件扩展名方式。所谓magic方式,就是根据文件内容来判断。绝大多数文件,内部都有一些特定的标记,这些标记称为magic,比如BMP图片文件以BM两个字符开头,BM就是一个magic。虽然即使采用了双保险机制也有误判的可能,但概率已经大大降低了。

2. 文件类型的表示。
文件类型如何表示呢?我们说JPEG是图片文件,说txt是文本文件,WML是XML文件。这种分类很直观,但也有几个问题:对JPEG文件来说,称它图片文件太笼统了。有的图片浏览工具虽然能够打开大部分图片文件,但不一定能打开所有图片文件,它需要更详细的文件类型信息。对txt和WML来说,它们其实都是文本文件,有的编辑器可能以同样的方式处理它们。为了避免分类太细或者太粗,linux采用了MIME(可以参考相关RFC)规范,它用一种层次型的方式来分类,如:
JPEG文件:image/jpeg
文本文件:text/plain
XML文件:text/xml
这种分类方式就可以粗细兼顾了。

3. 文件类型的数据信息。
在linux下,关于文件类型的信息通常放在/usr/share/mime、/usr/local/share/mime和用户目录下,所有应用程序可以共享这些信息。在该目录下,一般会有以下这些文件:
l aliases:文件类型的别名。比如application/pdf
有时也称为application/x-pdf 。
l
magic:各种文件的内部标识,用于从文件内容来判断文件类型。如BMP图片文件以BM开头。
l
globs:扩展名与文件类型的对应关系。如*.cpp文件是text/x-c++src类型的。
l packages目录:用于安装新文件类型用。
l
其它子目录及其下的文件:更详细的描述各种文件类型。比如image下的jpeg.xml文件描述了jpeg文件类型。为了方便国际化,这些描述信息有各种语言版本。

4. 图标文件与数据文件的关联。
在资源管理器中,通常用不同的图标来区分不同的文件类型。同时图标也是桌面主题相关的,主题不同,图标的大小和外观也不一样。图标文件通常存放在/usr/share/icons/主题/大小/mimetypes目录下。

文件类型与图标文件的对应关系是通过文件名来实现的。比如,JPEG文件对应的图标文件为gnome-mime-image-jpeg.png。
(这块不是很确定,有待进一步研究)

5. 应用程序与数据文件的关联。
应用程序与数据文件的关联是通过.desktop文件来实现的。应用程序要出现在开始菜单中或者桌面上,它要提供一个desktop文件才行。应用程序安装之后,desktop文件通常安装到/usr/share/applications下。

可以在desktop文件中,指明其可以操作的文件类型。如,软件包安装程序可以操作rpm文件,它的desktop文件(system-install-packages.desktop)内容为:
[Desktop Entry]
Name=Install Packages
GenericName=Install Packages
Comment=Install new packages on the system
MimeType=application/x-rpm;
Exec=/usr/bin/system-install-packages %F
Terminal=false
Type=Application
Icon=system-config-packages.png
Encoding=UTF-8
NoDisplay=true

MimeType项指明它可以操作rpm类型的文件。

Linux比windows的做法科学之处。
1. Linux采用了双保险机制,对文件类型的判断更正确,出错的概率更小。

2.
Linux分离文件类型判断信息和文件关联方式,这样文件类型信息可以被重用。比如file命令可以用这些信息来判断文件类型,而不必打开它。

参考资料:
http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-0.13.html
http://www.freedesktop.org/wiki/Standards_2fAddingMIMETutor

这四年来(以前写的,有点乱)

原帖:http://blog.csdn.net/absurd/archive/2006/02/17/601586.aspx

(刚到深圳时写的)
转眼就毕业快四年了,在一研究所呆了半年,在北京工作了三年,在深圳流浪了近三个月。
  
呆在研究所时,同从武汉过去毕业生共九个,无一不是感觉上当了。他们大多数人,要么一起谈论离职,与单位谈条件,要么就是怨天尤人。而我却只能把失望压在心里,老爸是中建七局公司的工人,凭着老爸高超的手艺和勤奋的工作,家里还算过得殷实。然而在我高考后几天,他为救一个同事,自己从三楼摔到二楼,腰部受伤,被迫病退了,加上弟弟同时上学,家里日趋困难。
  
去上班的路费还是向表哥借的,我还得等第一个月工资,以资助弟弟。再说我主修的是机电,辅修的计算机应用,没有工作经验,重新找份工作也很困难,下几个月的生活更无着落。
  
这时,女友打电话来说,大家工作在两地,生活在一起是不太可能的,你现在一个月几百块钱,自己生活都困难,还要资助你弟弟,你即使想来看我一次都不容易,还是分了吧。我握着电话,没有出声,任眼泪静静的流下。最后说了一句,等我一年,好吗?我发誓,一年后,月薪会超过三千的。她犹豫了一会儿,说,大家年龄都大了,家里人也着急,还是不用了吧。
  
性格本来就内行,我更加远离人群了,在宿舍里拼命的学英语,在办公室拼命的学编程。内心股强大的力量,不断的告诉我,一定要努力,一定离开这里。从不参与同事的抗议活动,也不参与他们的抱怨研讨会,只是默默的学习。有同事问我,不想离开吗?我笑着说,单位不倒,我是不走的,其实我比谁都想走。
  
渐渐的,凭着以前的数据库功底和PB的知识,不但可以轻松的应付工作,还可以指导同事。上司也是个爱学习的人,比我大两届,他见我如此爱学习,就让我专心的研究新技术,而不工作了。当时所谓的新技术无非是加密、反拷贝之类的。值得一提的是,他让我还要学习COM原理和OpenGL等知识,对此,他也仅懂一点,而我以前都没有听说过,他只是告诉我说,很有用的,同时给我买了十多本书(至今对他极为感谢,当时我根本没钱买书)。
  
现在想来真是搞笑,由于是全新的东西,很简单的概念都要想半天,翻译的也很烂,学习了半个月,还不清楚类工厂是做什么用的。直到看潘爱民老师的《COM原理》,才真正搞懂了。
  
终于到年底了,想到拿到那一千块的年终奖,就可以跑路了,不禁有些激动。为了谨慎起见,我装出一副平静的样子,只有在走了那一天,同室的同事才知道。
  
去北京之前,联系了在北航读研的好友老胡,同时发了五六封求职信。那时恰逢北京最冷的时候,零下十六度,又下很大的雪,只好花了三百块去买了羽绒服,又花了二百块去买了双鞋,还花了一百块买了个钱包,钱就用得差不多了,还好另外一个同学资助了一千块。
  
去了四家公司面试,都通过了。之所以后来选择那家公司,一是因待遇比较高,另一方面,在我发过简后,不到半小时就收到了他们的回复,真让我感动。老板让我去拿offer时,说,给你三天时间考虑,没问题的话,二月一号来上班,我当时兴奋的说,不用考虑了,一定来。
  
开始为了让我熟悉项目,让我写Linux平台的测试程序。旁边的同事,特别好,不厌其烦给我讲解(很想念他的,不知他现在过得好不好)。上司低估了我的能力,几个任务,我都只用比预期时间少得多的时间完成了,上司由此对我刮目相看。我完成了一套测试程序框架,工作量一下缩小到原来的五分之一,RD的人都无不惊异。
  
记得,给RD一个Linux高手讲了我的测试程序框架后,他看着我了老半天,才说,你太聪明了。我笑了笑了,说,惭愧,刚刚接触C++。其实心里高兴得不行了。
  
然而,后来的工作,却不是我想要的,前人留下的代码,写得也很差,本想成为编程高手的我,结果成了一个调试高手。工资也在长,职位也在升,然而我却日趋倦怠。感到在工作中学不到什么东西,有时你费尽心思找到一个严重的错误,不过是个简单的内存溢出,或者文件关闭后继续在使用。在上百万行代码中,而这种错误潜在的,不知有多少。我建议大家学习代码重构,设计模式,由于诸多原因无法实行。
  
真的累了,感觉就是在原地不断的奔跑,累了,然而还在原地。渐渐的萌生退意。后来公司组织一次活动去爬山,一件事让我很郁闷。
  
我一向很守时,说好早上九点半集合,我准时到了,而此时到的人不到五分之一,等到十点多,老板才过来。上山前,说好下午五点半集合,我计划好时间,估计五点可以下来,就和绿野的几个同伴一起走了,而最终五点十分回到集合地点,发现他们等了我一个小时间了,原来,他们只在半山腰转了一下,就回来了。晚上被老板训了一顿,说因为我而耽搁大家一个小时。真是郁闷极了,守时反而挨训,不守时反有理,真是天理何在。
  
每天在上地三街和七街间往返,早上在五街时,看见那班飞机从头顶飞过,在六街遇到几个中东的老外,还有一个总戴着红眼镜的女孩子。听着云飞的幽默集装箱或者CRI的Music。时间就这样溜走了。
  
和女友分手后,渐渐的我对周围的事充耳不闻,除了工作学习,个人外出旅游,偶尔和喜欢的网友聚一下。在公司里,我的脾气还是那样好,大家都喜欢和我来往,然而仅仅限于工作上的来往罢了。记得当时QA有个女孩,真的很喜欢她,看得出她对我也好感。然而另一个同事捷先登了,结果那女孩被吓跑了,一直很后悔没有早点行动。
  
年前给老妈打电话,老妈问起那个老问题,找对象没有。我说,没有。她说,二十六了,你小学同学的儿子都上小学了,该想想了。二十六了,我真的不敢面对时间流逝,年龄的增长。
  
二十六了,我还是一无所成!生活还是那样单调!我决定走了,决定去南方,我拒绝了亲友的忠告,拒绝了老板的加薪和升职,拒绝了同事的挽留(美国那边两个同事,一再劝我不要走,最后又劝我在外不好,就再回来)。
  
到了深圳,见到好友时,他第一句话就是,你还是像刚毕业时那样年轻。我苦涩的笑了笑,其实内心感觉好老了。他看了看我在背上的小包,说,就这么点东西,你是来玩的吧?我说就这点行李。他又说有钱就行。我又笑了,我存折上总共还不到八千块钱。
  
自信自己的能力,不在任何同龄人之下,尽管这几年IT不景气,还是相信可以找到一份新的工作。然而发出的数十信简历竟如沉大海。
  
我有些沮丧,这时一个好友拉我入伙,和他们一起做嵌入式系统,他们几个清华毕业的校友,软硬都很厉害。我答应做兼职,尽管没做过,凭着在学校时基础,很快上手了,开发出一些网络组件和图形组件。
  
渐渐的,觉得一个人在家里做,还是不如到公司做有感觉。这时另一个好友,介绍我去华为。记得,在面试那天,旁边一个小孩和我说话,他说,你也是去年毕业的吧,我笑了笑说,2000年毕业的。他惊讶了半天,说,不会吧,看起来这么年轻,怎么会那么老呢?呵,我只好苦笑。
  
面试很顺利,得到各方面的评价都比较高,不久就收到华为的offer了。一直羡慕华为的规范,所以铁了心要去了。然而,在腾讯的好友又介绍我去他们公司,说腾讯的待遇比华为高得多。今天又接到步步高和恒伟业面试的通知。我想凭自己的能力,都可以通过吧。
  
真是有些郁闷,想找工作时,竟没有人理我,找到工作时,又有很多选择了。我的性格内向,向管理方面发展,空间很小,做技术吧,空间本就不大,和朋友创业吧,自己没有创业头脑,也只能跟着他们混。

何去何从,真是有让人为难。

如何面对单调重复的任务

转载时请注明出处:http://blog.csdn.net/absurd/

我们每个人都喜欢做有挑战的,能学到新东西的任务,而不愿意去那些单调重复的,没有什么新意的事情。然而常常事与愿违,在软件开发中,前者并非主流,而后者占了大多数。前者未必每次都能轮到你,而后者也总是要人完成的。

面对后者,你可以选择拒绝接受任务,但那会让人觉得你工作态度不好,以后好的差事可能就轮流不到你了;你可以选择走人,换个地方去做,但那也只能祷告,祈求上帝保佑你在别的地方遇到好任务。其实这都不是好的做法,相反我们应该接受并搞掂它,应该想法转换它们,完成任务但又不必自虐,从中还能学到东西。下面是我的一些经验:

1.
让电脑去做单调重复的工作。Unix文化有一个原则:宁愿花机器一分,不花程序员一秒。单调重复的工作多数都是有规律可循的,有规律可循就可以让电脑来做。

实例一:在早些年代,那时还没有听说autobuild这个概念,发布版本是一件痛苦事情。你要从CVS上取出源代码,编译各个版本((英文版,
日文版) x(大企业版, 小企业版,
试用版)),再制定各个版本的安装包,最后上传到FTP服务器上。如果整个过程顺利,四个小时差不多了,但事实是从来没有顺利过,结果通常要花费两三天才能完成。编译出错,手工拷贝文件出错,上传时放错目录如此等等。那位负责做版本的大姐还算有耐心,坚持做了大半年时间,后来这事没费多大劲就推到我头上了(呵,大家都认为我好欺负)。我接手后,第一件事是花了两天用bash写了个脚本,把整个过程自动化了,在接下来一个版本中进行了验证,并修正几个脚本里的问题,后来发布版本时几乎不用人干预了。

实例二:如果有人问我写得最多的程序是什么。我一定会回答是代码产生器,前前后后、大大小小至少写过十几个代码产生器,小的可能是用bash+awk来做的,也就上百行代码,大的用C/C++来写,动则数千行代码,最大的竟达8000多行C++代码。大部分代码产生器都为我节省了不少时间,或者至少把单调重复的事情变得有趣一点了,而且得到的代码也更稳定可靠。

2.
换种思路,看有没有捷径。有些事情单调重复,本来也是可以让电脑去做的,但是开发相应的工具要费更多的时间,得不偿失,这时不换一种思路,或许别有洞天。

实例一:曾接到一个任务,要求找出一个公共函数库里的所有全局变量。那个库是个大杂烩,里面什么东西都有,凌乱而庞大。时间期限是一周,时间比较充足,即使一个文件一个文件的去找,时间也来得及,但那太痛苦了。更麻烦的是这个库是变化的,可能刚刚完成任务,又有人加了一个全局变量,这样就很难拿到一个最新的结果。怎么呢,我想,编译器肯定是知道哪些是全局变量的,所以第一反应是拿一个开源的编译器修改一下,让编译器告诉我结果。修改编译器可能也要一周时间,但利用它随时可以得到最新结果。有没有更简便的办法呢?猜想VC输出的map文件或许有些帮助,打开map文件一看,果然如此。让VC编译该库并输出map文件,取出全局变量列表,搞掂了。从接受任务到完成任务前后不到一个小时。

实例二:前几天,同事拿一个第三方库,编译时发现那个库是用带硬件浮点数的toolchain编译,而我们的toolchain用的是软浮点数。尽管反汇编出来,没有发现浮点指令,但编译器就是不让链接该库。我们的方案是,反汇编它再重新编译它。但反汇编出来的格式与as的输入格式有些差异,要花不少时间去修改,修改之后发现还是编译不过去。最后,这个任务又落到我头上了。我想既然没有用浮点指令,编译器不让编译可能是因为一个标志引起的,于是花了点时间去研究ELF(linux下的可执行文件格式)文件格式。果然是文件头中一个标志引起的,写了个小程序为该库加上这个标志位,编译就通过了。

3.
换种心情,坦然接受。如果面对一项任务,你别无选择时,那就坦然接受它吧。然后想法说服自己,让自己有个好的心情,这样的任务总是要有人做才行。还可以告诉自己,一定要从中学到点东西,即使从中学不到技术,也要从中学会忍耐。

当我从QA组进入RD组时,我任务是把Win32程序移植到linux下。这个任务比较重要,但绝不是什么好任务,工作本身单调不说,别人还瞧不起。他们认为这都是很简单的,不用动脑子的体力活。同组的同事很多都走了,新来的同事也呆不了多久。但我坚持下去了,移植的同时去研究那些代码,去研究Win32和linux在编程上的差异,
一年之后我成了少数几个了解整个系统架构的人,编程能力大有提高,对软件的可移植性也有了较深的理解。

~~end~~
如何面对单调重复的任务

转载时请注明出处:http://blog.csdn.net/absurd/

我们每个人都喜欢做有挑战的,能学到新东西的任务,而不愿意去那些单调重复的,没有什么新意的事情。然而常常事与愿违,在软件开发中,前者并非主流,而后者占了大多数。前者未必每次都能轮到你,而后者也总是要人完成的。

面对后者,你可以选择拒绝接受任务,但那会让人觉得你工作态度不好,以后好的差事可能就轮流不到你了;你可以选择走人,换个地方去做,但那也只能祷告,祈求上帝保佑你在别的地方遇到好任务。其实这都不是好的做法,相反我们应该接受并搞掂它,应该想法转换它们,完成任务但又不必自虐,从中还能学到东西。下面是我的一些经验:

1.
让电脑去做单调重复的工作。Unix文化有一个原则:宁愿花机器一分,不花程序员一秒。单调重复的工作多数都是有规律可循的,有规律可循就可以让电脑来做。

实例一:在早些年代,那时还没有听说autobuild这个概念,发布版本是一件痛苦事情。你要从CVS上取出源代码,编译各个版本((英文版,
日文版) x(大企业版, 小企业版,
试用版)),再制定各个版本的安装包,最后上传到FTP服务器上。如果整个过程顺利,四个小时差不多了,但事实是从来没有顺利过,结果通常要花费两三天才能完成。编译出错,手工拷贝文件出错,上传时放错目录如此等等。那位负责做版本的大姐还算有耐心,坚持做了大半年时间,后来这事没费多大劲就推到我头上了(呵,大家都认为我好欺负)。我接手后,第一件事是花了两天用bash写了个脚本,把整个过程自动化了,在接下来一个版本中进行了验证,并修正几个脚本里的问题,后来发布版本时几乎不用人干预了。

实例二:如果有人问我写得最多的程序是什么。我一定会回答是代码产生器,前前后后、大大小小至少写过十几个代码产生器,小的可能是用bash+awk来做的,也就上百行代码,大的用C/C++来写,动则数千行代码,最大的竟达8000多行C++代码。大部分代码产生器都为我节省了不少时间,或者至少把单调重复的事情变得有趣一点了,而且得到的代码也更稳定可靠。

2.
换种思路,看有没有捷径。有些事情单调重复,本来也是可以让电脑去做的,但是开发相应的工具要费更多的时间,得不偿失,这时不换一种思路,或许别有洞天。

实例一:曾接到一个任务,要求找出一个公共函数库里的所有全局变量。那个库是个大杂烩,里面什么东西都有,凌乱而庞大。时间期限是一周,时间比较充足,即使一个文件一个文件的去找,时间也来得及,但那太痛苦了。更麻烦的是这个库是变化的,可能刚刚完成任务,又有人加了一个全局变量,这样就很难拿到一个最新的结果。怎么呢,我想,编译器肯定是知道哪些是全局变量的,所以第一反应是拿一个开源的编译器修改一下,让编译器告诉我结果。修改编译器可能也要一周时间,但利用它随时可以得到最新结果。有没有更简便的办法呢?猜想VC输出的map文件或许有些帮助,打开map文件一看,果然如此。让VC编译该库并输出map文件,取出全局变量列表,搞掂了。从接受任务到完成任务前后不到一个小时。

实例二:前几天,同事拿一个第三方库,编译时发现那个库是用带硬件浮点数的toolchain编译,而我们的toolchain用的是软浮点数。尽管反汇编出来,没有发现浮点指令,但编译器就是不让链接该库。我们的方案是,反汇编它再重新编译它。但反汇编出来的格式与as的输入格式有些差异,要花不少时间去修改,修改之后发现还是编译不过去。最后,这个任务又落到我头上了。我想既然没有用浮点指令,编译器不让编译可能是因为一个标志引起的,于是花了点时间去研究ELF(linux下的可执行文件格式)文件格式。果然是文件头中一个标志引起的,写了个小程序为该库加上这个标志位,编译就通过了。

3.
换种心情,坦然接受。如果面对一项任务,你别无选择时,那就坦然接受它吧。然后想法说服自己,让自己有个好的心情,这样的任务总是要有人做才行。还可以告诉自己,一定要从中学到点东西,即使从中学不到技术,也要从中学会忍耐。

当我从QA组进入RD组时,我任务是把Win32程序移植到linux下。这个任务比较重要,但绝不是什么好任务,工作本身单调不说,别人还瞧不起。他们认为这都是很简单的,不用动脑子的体力活。同组的同事很多都走了,新来的同事也呆不了多久。但我坚持下去了,移植的同时去研究那些代码,去研究Win32和linux在编程上的差异,
一年之后我成了少数几个了解整个系统架构的人,编程能力大有提高,对软件的可移植性也有了较深的理解。

~~end~~