程序员的好伙伴

介绍 Windows 环境下的文本编辑器,命令行的使用,版本控制软件以及相关话题

最后更新 2013.4.24

引言

就我接触到的程序员和计算机专业的学生来看,大多数对于计算机都有着比“工作”更高一层次的感情。然而出乎意料的是有不少人虽然在编程相关专业知识上花很多时间学习和研究,但是在日常使用上却较为疏忽。举几个例子:

  • 使用系统自带的 Notepad, Microsoft Word 或者打开臃肿的 Visual Studio 来查看单独一个程序源文件
  • 总是从 开始-附件-命令提示符 来开启命令行,在一直cd xxx到目标目录,调试程序时将程序输出结果从命令行输出拷贝到文本文件中再来处理
  • foo.cc 复制并重命名为 foo.cc.bak, foo.cc.old, foo.cc.bak2 这样来进行修改和备份

为了避免这些折磨人的问题,这里我想整理下我认为对各位程序员有帮助的经历,希望能够帮助到大家。本文中谈到的内容全部都是针对 Windows 平台。

你需要一个好的文本编辑器

对于经常处理代码的用户来说,一个好用的文本编辑器应该满足以下要求:

  1. 启动快,资源占用少
  2. 具有高级的功能,如有单窗口多tab显式,查找高亮,不锁死编辑文件
  3. 有方便处理代码的功能,如行号显示,语法高亮,支持不同换行格式,显式支持不同编码

下面马上就会提到几款好用的编辑器让你选择,但在这里我想说说为什么系统自带记事本或者你机器上装好的 VS200x 不行。我相信大多数人进行开发的时候都是使用功能完本的 IDE 。但偶尔你应该也会只是想打开几个代码文件,看看里面的内容,有时进行简单的编辑之后马上就关掉。当 IDE 显得太过杀鸡用牛刀的时候,这种文本编辑器就很能派上用场了:有着 IDE 类似的编辑界面和显示方式,功能够用启动和退出又不用听硬盘响;系统自带记事本除了缺失编程辅助功能以外,还有些问题很有可能让你遇到意料之外的情况:

  1. 记事本不支持 Linux 换行符,有些时候你打开某些文件发现里面所有字符都缩成一行那十有八九就是因为它使用了 Linux 式换行符。再用记事本处理会变得更为麻烦。
  2. 记事本没有显式的表示文件的编码,一方面处理中文文件碰到乱码不知道如何处理,另一方面对于某些编码需要指定的情况记事本会很糟糕。
  3. 记事本不会监测已打开文件在别处是否被修改。

上面的第三条有必要在这里解释一下。你可以试试建立一个新的文本文档,然后用两个记事本打开这个文档。然后再第一个记事本中输入些内容,然后保存,接下来在第二个中保存(其显示应该是空的),现在两个记事本都关掉,注意此时没有任何提示,能正常退出。然后再打开这个文件,你会发现其内容是空的。当上面这种情况在你用记事本处理文件时已某种形式重现的时候,你肯定会泪流满面。这不是一个设计良好的软件应该具有的行为,实际上使用下面提供的编辑器都会在某个时候提示文件内容被修改。

嗯,如果看到这里你已经决定要换一个编辑器的话,这里有不少很棒的选择:

优秀的文本编辑器

下面提到的几个文本编辑器在启动速度,内存占用甚至是功能上都大同小异。各位请挑个看着顺眼用着顺手用的吧。

Programmer's Notepad

把它放在第一绝不是按字母排序~ 比起后面几个软件,Programmer's Notepad 发布较晚,相对也没有那么出名。但其闪光点也蛮多:界面简单漂亮,功能足但菜单和选项都十分简洁,不会让人疑惑;可以用 Python 写脚本扩展。开发者更新也很勤快。要说缺点的话,可能还是名字稍微逊了点。

Notepad++

Notepad++ 应该是这几个里面最为出名的一个。项目的开发在03年之前就开始了,现在算是资历比较老的开源文本编辑器了。特点是功能全插件多,每个菜单打开都是满满一列的菜单项。作者应该是相当有立场的一个人,你看他项目网站上的新闻经常会有些表达态度的文章,当年SourceForge在大陆无法访问的事情也跟他有联系。

Notepad2

这个应该算是软件里"minimal"风格的代表,本身只有一个200k的可执行文件和一个配置文件,也不支持插件扩展,但代码相关基本功能都不缺,还有半透明,总在最前这样方便的小功能。

除了上面提到的,在收费共享软件里还有 EmEditor, EditPlus 应该比较出名,但说实话我个人认为跟上面这几个差异不大。更高级的当然还有 VimEmacs,作为终极的文本编辑解决方案,相信你总有一天会碰到它们。而且虽然这上面介绍了这么多编辑器,但我这篇文章其实还是在 Vim 里敲出来的...

Sublime Text

我现在已经完全转到使用了 Sublime Text 3。在此之前我一直勤勤恳恳的在 Windows 上使用 GVim。我仔细想了想为什么,大概归结于下面几点:

  1. Sublime Text 有很好的 Vim 键位模拟支持。下下来改一行配置感觉就像回家了一样,这对于 Vim 用户实在太有吸引力了。我经常用的操作全都有,甚至 q 键上的 Macro 都可以用,实在是太棒了。
  2. 功能强大丰富的同时,有合理的默认配置。现在用下来的感觉跟我之前用 Vim 感觉几乎差不多,我原来离不开的功能全都有,比如基于打开窗口的补全,自动符号索引和根据符号的跳转(Sublime Text 3里新加入的)这些一个不落下。然而我自己改的配置加起来不到20行,安装的 Plugin 只有两三个。比起 GVIM 里两百行我都不知道是啥的配置和加起来估计有十兆还不知道能否正常工作的插件,感觉就方便很多。
  3. Sublime Text 作为一个文本编辑软件,自己是非常有竞争力。启动飞快,界面超酷,界面响应够快不卡死,自带简单够用的 Project 系统,简单易懂易更改的配置系统,还有个基于 Python 的插件系统甚至有一个第三方的插件管理器。

Sublime Text 2 在不购买的情况下可以全功能无限使用,只是会周期性弹购买提醒。而 3 还在 Beta 中只有购买的用户可以用。我建议你下下来先感受一下,如果可以的话用上一段时间。如果你觉得它给你节省时间的价值达到了它的售价,那个时候就果断买一个吧。

谨防有人还在用宋体来阅读代码...

虽然没有哪里有明确的规定,但是程序员看程序一定要使用等宽字体(monospace font)。我们看中文的时候可能感觉不明显,但电脑上的英文字体大部分都是非等宽的,也就是一个 A 和 一个 a 的宽度明显是有差别的。代码终究不是自然语言,它更为结构化层次化,在显示上等宽字体在这方面得到的好处更多。而且现在等宽字体的设计都考虑到程序员的需要,对于相似字符都有刻意设计差别,让人更为容易辩解。还有更为重要的一点,如果有别的程序员看到你用非等宽字体阅读代码,他会气得抓起板凳打你的头!!!

总而言之,为了对自己好一点,请一定选择等宽字体来阅读代码。但在这里还有一点需要再多讲一下。在 Windows 环境下字体总会牵扯到一个额外的问题,就是其特有的 ClearType。简单讲系统开启了 ClearType 的话,在字体渲染的时候会进行一些处理,目标是让字体变的更好看。直接的结果就是字体在开启和没有开启 ClearType 的情况下,显示效果有着非常大的区别。下图中字体为 Consolas 10pt, 左边是开启了 ClearType 的情况,而右边是没有开启的。(图片来源)

Consolas 对比

这里要表达的其实是,开启 ClearType 前后同一个字体会是两个表现,所以在挑选字体的时候一定要考虑到这个情况。往往 TrueType 字体比较适合 ClearType,而点阵字体比较适合原始的设定。Windows XP 默认是没有开启 ClearType ,而 Windows 7 则一上来就是打开的。相关修改方法请自行搜索,这里有这个话题相关的几篇文章:

另外要提到的是现在网上一直流传着一个叫做 Yahei Mono 的字体,一般就是把微软雅黑和某种等宽英文字体结合起来,效果还挺好。如果你有大量中文写作的需求不妨找来试试看。

命令行

经常接触代码相关的工作,我们应该比一般 Windows 用户有更多机会接触到命令提示符(command prompt)。作为高端计算机用户,你不能总是把它称为"那个黒黒的窗口"或者"DOS模式"。命令行永远是程序员的好朋友,我们需要更加了解它。

Windows 命令行基础

谨防有人完全没有接触过这个,这里列出相关的一些基础知识

  • 你可以在 开始菜单-> 所有程序 -> 附件 -> 命令提示符 来打开 Windows 下的命令行界面。或者,按下快捷键 Win + R,在弹出的运行窗口中输入 cmd,这样会更叼。

  • 在命令行窗口中,你通过输入指令,按下回车执行。常见的包括 dir,列出当前目录下的所有文件,cd 切换当前目录。

  • cd .. 返回上一级目录,当然你也可以指定任意一个存在的目录比如 cd C:\Foo\Bar。你会发现 cd 其他磁盘时没有效果,这时你需要输入分区符来切换分区,如 d: 将切换工作目录到 d 分区下。

  • 除了系统自带的命令以外,如果当前工作目录下(或者 Path 中,下文详细讲解)有可以再命令行环境下执行的程序,那么输入它的名字也可以执行他。这个在下面的例子中也有提到。

命令行是什么

在操作系统出现之前,计算机除了执行指定的运算程序之外,没有多余的计算能力来处理更多问题。当硬件发展到一定程度后,操作系统作为硬件和用户之间的桥梁开始出现了。以 Unix 为代表,操作系统除了提供应用程序编程接口 (API, Application Programming Interface)给程序员,通过编写程序的方式来指挥硬件工作。另一方面,早期的操作系统另外提供了命令行界面 (CLI, Command Line Interface)给用户,用户可以通过输入指令来控制系统执行哪些程序,进行磁盘操作,关机重启等等。在 Windows 下,命令提示符应该就能够看做是 Windows 提供给用户的一个命令行界面。下面有一个 C 语言的例子,希望能通过它来让大家更好的理解:

#include <stdio.h>
/* lnecho : echo arguments in lines */

int main(int argc, char* argv[]) {
    int ix;
    for(ix = 0; ix < argc; ++ix) {
        printf("%s\n", argv[ix]);
    }
    return 0;
}

你可以把这段代码保存为 main.c 。你机器上应该安装了 Visual Studio 的一个版本。在这里我们用它提供的命令行工具来编译这个文件。首先,在开始菜单里找到 Visual Studio 的项目,以 VS2008 英文版为例,找到 Microsoft Visual Studio 2008 > Visual Studio Tools > Visual Studio 2008 Command Prompt。这样就会出现一个命令行窗口。之后,我们需要将工作目录更改为之前 main.c 保存的目录。更改目录可以通过 cd 指令来进行,例如 cd C:\Temp。到了这里,我们输入这样一行指令,调用 VS 的编译器来将我们的代码编译成可执行文件:

cl /Felnecho.exe main.c

之后输出的结果我也放在这里:

Microsoft (R) 32-bit C/C++ Optimizing Compiler
Copyright (C) Microsoft Corporation.

main.c
Microsoft (R) Incremental Linker
Copyright (C) Microsoft Corporation.

/out:lnecho.exe
main.obj

输入 dir 指令,命令提示符下会列出当前目录下的所有文件,我们会发现出现了一个新增的 lnecho.exe,这就是上面代码编译出的结果。在这里我们可以试试它的功能:

C:\Temp>lnecho hello command line
lnecho
hello
command
line

到这里这个例子就结束了。我们倒过来看看到底发生了什么。首先看看我们 main.c 里面的代码,main 函数里的参数其实就是对应的该程序在执行时系统传入的参数(arguments),这个参数是对于整个程序而言,且是在运行时才决定的。在上面执行的 lnecho hello command line中,argc 是参数的个数,在此为 4,要注意程序自己的名称也算是一个参数,而且永远是第一个。而 argv 则是储存对应的参数字串。系统总是用空格来分割每个参数,所以 argv[0]lnecho\0 (以传统 C 风格的字串符方式存储), argv[1]hello,以此类推。剩下的就比较清楚了,程序里将传入的参数逐行输出之后结束,我们给它起个专业点的名字叫 lnecho (line echo)。

环境变量,及其他

还是接着上面的例子,我们这里再讲讲 Windows 下命令提示符中其他几个重要的方面。之前我们要用 VS2008 中的编译器来进行对代码进行编译,于是我们通过开始菜单中一个特殊的快捷方式打开了命令提示符。而它做的其实就是重新设定了当前命令行窗口的环境变量,让系统能在正确的地方寻找到 cl,也就是我们用来编译代码的那个程序。

这里我们先打开一个新的命令行窗口,你可以试试按下 Windows键 + R 键,这样会呼出运行窗口,输入 cmd 并回车,这样挺方便又显得很专业。之后在命令行窗口中输入 set。我把我机器上的的部分结果放在这里:

ALLUSERSPROFILE=C:\ProgramData
CCHZPATH=C:\CTEX\CTeX\cct\fonts
CCPKPATH=C:\CTEX\CTeX\fonts\pk\modeless\cct\dpi$d
....
LANG=zh_CN
Path=C:\Bin;(......)
....
windir=C:\Windows
VS90COMNTOOLS=c:\Microsoft Visual Studio 9.0\Tools\
....

这里每一行对应的就是一个环境变量。其中等号左边就是变量名字,而右边则是变量的值。我们可以试试输入 echo %Path%, 输出结果应该就是 Path 对应的值。我们这里看到环境变量大都是路径,这也说明了环境变量的用途。拿 Visual Studio 举例子,安装时我们可以把他安装在任意一个目录,C 盘 D 盘都可以,那么程序中如果要确定 Visual Studio 某个部件在哪里的话要根据目录来确定的话其实是很麻烦的,这里就可以为其设定一个环境变量,例如上面的 VS90COMNTOOLS,程序可以通过这个环境变量来获得它的具体路径。而实际上环境变量中存放任何字串都是可以的,你可能在其中看到类似版本号,名称等等。

在众多环境变量中有一个特别的叫做 Path,它牵涉到系统查询路径的方式。例如你在命令提示符中你输入 lnecho 并执行时,系统首先会在当前工作目录依次寻找 lnecho.exe, lnecho.bat 等等可能的可执行文件,如果找不到,则系统会依次Path 中提到的每一个目录(它们以分号分隔)中寻找是否存在 lnecho.exe, lnecho.bat 等等。如果说我觉得 lnecho 挺好用的,我可以在建立一个 C:\Bin 目录,把我们的 lnecho.exe 放在这个目录当中,并将 C:\Bin 加入 Path 中,这样我们就可以在任意目录都能调用 lnecho 命令。事实上我也推荐你这么做,下文会提到很多有意思的命令行工具,你可能需要一个方便的地方来存放它们。

在 Windows 下设置系统变量的具体步骤是:

  1. 打开 我的电脑/计算机 -> 属性 -> 高级系统设置,点击环境变量
  2. 系统变量中增加,编辑或者删除。这里再配上张图片以防有人找不到。

设置环境变量

经过设置后系统会在每个命令提示符运行时都将这些环境变量设置好。我们之前执行的 VS2008 提供的命令行工具他所作的其实也就是在当前运行的命令提示符窗口中一次性的设置好环境变量,这样你可以在其中正常使用 VS 中的工具,且不会影响系统中的环境变量设置。

实用工具及技巧

上面讲了这么多,但估计大家还是没有觉得使用命令行有什么好处。其实你应当有感觉,编程期间偶尔还是躲不过与命令行接触。在这里我想介绍下一些方便的命令行工具,和一些有意思的小技巧,希望对大家有些帮助。

实际上 Windows 自带的命令行提示符功能缺失很严重。微软应该也是意识到了这一点,在 Windows 7 开始加入了 PowerShell 来尝试提供一个新的 Windows 下的命令行环境。不过事实上对于现有的命令提示符,我们有办法能让它变的更为有好。

首先你可以试试调整命令提示符的显示,右键单击命令提示符的窗口选择属性,你会发现你可以简单的更改窗口大小以及字体颜色。不妨花点时间将它调整到自己觉得舒服的状态,毕竟黑底白字看起来的却很压抑。

之后我们要知道命令提示符有很多方便的功能,如果你不知道的话就会很郁闷:

  • 在命令提示符下常常需要输入文件名,事实上如果你打好了文件名前一部分的时候,按 Tab 键后是会帮你补全的。比如你常常要 cd 来变换工作目录,假如你当前目录下有一个叫做 very_long_name_folder,此时你只要键入 very 或者更少的部分后,按下 Tab 键就会发现上面的 very_long_name_folder 已经被补全了。现实生活中我还经常看到有人默默的打全整个文件名的,当我告诉他可以按 Tab 补全的时候他常常是泪流满面...
  • 你应该还记得学习 C++ 的时候,你应该还记得 cincout 分别是对应标准输入标准输出。用到 cin 的时候我们是可以在命令行里通过键盘即时输入的,而用 cout 则是打印到命令行窗口中。事实上标准输入输出在命令行中是可以进行重定向的。使用管道 |,可以将前一个程序的输出结果作为后一个程序的输入,我们再后面可以看到例子。使用 > 可以将程序的输出重定向写到文件中。譬如 set > env.txt,就能将系统的环境变量存到当前目录下的 env.txt
  • 在两个命令之间加入 && 可以顺序的执行两个程序,比如 cd .. && cd .. 就会在返回到父目录的父目录。但事实上你使用 && 偶尔会碰到些莫名的问题,我们后面会提到编写 bat 脚本来进行连续的操作。
  • 我们知道如何开启一个命令控制符,并 cd 到需要的目录中。如果你突然想打开一个资源浏览器来打浏览这个目录的内容,或者进行一些简单的操作的时候,你可以输入 start .(注意有个点),这样就会在当前目录打开一个资源浏览器窗口,很方便的哟。
  • 如果你熟悉 Linux 的话你会发现 Windows 下的控制选项有些不同,它一般是使用 / 来加控制 flag 的。最典型的,如果你要获得某个命令的帮助,比如 shutdown 这个关机指令,你可以输入 shutdown /? 来获得帮助。

再之后我们得找些好用的命令行工具。我们知道在 Linux 的世界中命令行相比于 Windows 占着更为重要的一个位置,使用 Linux 下的命令行工具应该是一个好办法。要这样做其实有很多成熟的方法,比如使用 cygwin,在 Windows 下模拟一个完整的 Linux 环境,不过这样稍显臃肿,一般用户可能觉得难以接受;或者使用移植到 Windows 上的命令行工具,这样的方式简单方便,我们这里就采取这种方式。

有一个著名的开源计划 GnuWin 维护了一整套移植到 Windows 平台的 GNU/Linux 下的命令行工具。不过事实上作为初学者我们用不到那么多东西,这里有一个非常棒的替代品 Gow,它打包了一小部分常用的工具并提供了一个仅仅 5mb 左右的安装包。本文中就以 Gow 来进行接下来的讲解。

Gow 里的工具及使用

在这里 下载最新版本的安装包执行文件,之后按步骤安装即可。默认情况下 Gow 除了安装工具之外,还会帮你设置好 Path,并提供一个方便的功能:右键单击任意一个文件夹,你会发现在弹出的菜单中多了一个 Command Prompt here,也就是在当前目录中打开命令提示符,这个说实话挺方便的,避免了很多一直 cd 的情况。下面我们逐项介绍其中部分工具的功能:

  • gow -l 能够列出 Gow 其中包括的所有内容。如果对其中某一项的用法不清楚,你使用 Linux 通用的方式,加上 -h 或者 --help 来取得它的帮助。比如我不太清楚 comm 是干嘛的,执行comm --help 则能去的帮助。
  • 如果你对 Linux 有一点点了解的话,你会发现 ls, mv, cp 这些基本的 Linux 命令现在都有了。事实上 Gow 几乎包括了所有我记得住的 Linux 下的基本命令... 一方面这些命令能让你在两者间换来换去能使用同一套基本命令,另外这些命令也提供更多的选项来应对更为复杂的需求。比如 ls -1 > filelist.txt,这里 ls -1 列出当前目录下的所有文件和文件夹,每行输出一个,> filelist.txt 的作用是将前者的输出保存到文件 filelist.txt。这样你就获得了当前目录的一个清爽的列表。
  • 首先介绍最基本的 catgrep。来看看 cat text.txt | grep foo 这样一个命令,其中 cat text.txt 是将当前目录下的 text.txt 文件的内容输出到标准输出,而 grep foo 则是在标准输入中寻找带有 foo 字串的行并将其输出。grep 事实上非常好用,比如上面我要查询环境变量中的 Path,那么用 set | grep Path 就能获得结果。
  • head, tailcat 类似,但两者默认只取文件的前/后 10 行。而 tail -f logfile.log 对于正在不断写入到尾部的文件可以保持跟踪最新的结果,这个有些时候也很好用。
  • 有些时候你想将某段输出重定向保存为文件,但是这样的话就不会显示在命令行窗口中了。这里使用 yourprogram | tee output.txt 就能既将输出结果保存,又能在窗口中看到。tee 的意思其实就是字母 T 的读音,意义就是在输入输出中额外拉出来一段,挺形象的。
  • 某些程序的输出一出来就是一大堆,你不得不拉动滚动条回头去看,这种情况可以重定向输出到 less,比如 lotofoutput | less,这样你能在一个窗口内通过按键上下浏览输出结果。
  • sleep 正如其名字,跟其他命令一起使用可以达到延迟的效果。比如你要延迟 100 秒后关机,可以输入 sleep 100 && shutdown(不过其实 shutdown 自己就有延时的选项)。
  • 额外的,其实 Windows 自带了很多有用的命令行工具,比如 clip,你可以将通过 | 导入 clip 的输入,这样就会将结果拷贝到系统剪切板。还有 where,输入 where where 你可以看到 where.exe 的具体路径。

clink 好处多多

这边再插入介绍一个最近发现一个非常棒的东西: clink。这个在对 Windows 颇为简陋且限制颇多的命令行环境增加了很多方便的功能。比如用 Ctrl-V 粘贴,更棒的 Tab 自动补全路径和程序名字(会像Bash里一样列出一列来!),用 ! 来重复之前的命令,比如之前你输入了 cd a/b/c/d/e/f/g/h 那么下一次你只要输入 !cd 它就能帮你往回找最近的以 cd 开头的命令并重新执行。你下下来试试就会想为什么这么多年都没有人把这些功能做起来,好在现在有它就能解决不少问题。

使用上只要下载安装包后装上即可,记得要勾选自动挂载到 cmd 启动上,这样不管从哪里启动的 cmd 都能享用这些高端的功能。

其他有意思的命令行工具

如果你像我上面讲的那样,建立了一个 C:\Bin 并将其添加到 Path 中了的话,那么这里有几个有趣的小工具。Windows 下的命令行工具往往都是单独一个 .exe 文件,这样你可以把它们放到之前的目录中,直接在命令行里使用他们。

cloc

你有么有想知道你某个目录里的代码一共有多少行? cloc 这个工具就能帮你轻松,准确的计算某个目录下代码的行数。作为例子,我们看看 Quake 2 源码一共有多少行。你可以看到它会按语言来分来,并区分空行,注释和代码。

C:\quake2>cloc src
     479 text files.
     432 unique files.
      93 files ignored.

T=31.0 s (11.5 files/s, 7508.2 lines/s)
------------------------------------------------------------------------
Language              files          blank        comment           code
------------------------------------------------------------------------
C                       218          29507          23975         133278
C/C++ Header             88           3246           3245          18105
Assembly                 22           2235           2170           8333
....
------------------------------------------------------------------------
SUM:                    355          36232          30065         166456
------------------------------------------------------------------------

junction

Windows 下一直没有像 Linux 那样的软文件链接。幸好 NTFS 磁盘格式的新特性能让我们对文件夹进行这样的操作。

C:\>junction C:\linked C:\Windows

Junction v1.06 - Windows junction creator and reparse point viewer
Copyright (C) 2000-2010 Mark Russinovich
Sysinternals - www.sysinternals.com

Created: C:\linked
Targetted at: C:\Windows

之后你可以代开 linked 文件夹,你会发现它里面的内容跟 Windows 文件夹里的内容一摸一样。事实上这里 junctionWindows 映射到了 linked 文件夹。总有一天你会需要用到这个的,相信我。要删除映射的文件夹必须使用 junction -d 来完成。

mongoose

估计是 Windows 下建立 HTTP 服务器最简单的办法应该就是这个了。下载好放到 PATH 上后,在你想 serve 的目录下运行 mongoose(这里省略了版本号),打开浏览器访问 http://localhost:8080 就搞定了。你可以看看它的文档,功能还是很齐全的。

批处理 Batch

相信大家都应该见到过以 .bat 作为后缀名的可执行文件,这个就是 Windows 下的命令行脚本文件,批处理文件(Batch files)。命令行比起现在的图形界面,仍然保持着一个非常显著的优点,就是非常容易将操作自动化。你在图形界面下进行的操作,主要是靠鼠标在根据上下文的情况下在不同位置进行点击,这样一来要将其准确的记录并重现其实很复杂,目前在 Windows 下虽然有按键精灵以及高端点的 AutoHotkey 来完成这种操作,但说实话还是挺麻烦;然而如果你能将你的操作在命令行下完成的话,那么你下一次要执行这些操作只要重新输入同样的命令就可以了,命令本身就是字串,要记录和重现都非常方便。而批处理文件正式满足将命令行下的操作自动化的需求的:将你所需的命令操作按行写在以 bat 为后缀的文本文件里,然后双击执行,或者在命令行里输入它的名字就可以了。

这里有一个很现实的例子:本站点的更新由于各种限制,只能用 FTP 来完成。用 FTP 客户端软件虽然可以,但反复操作来还是挺麻烦,而且偶尔会漏传文件。于是我弄了一个 bat 文件,自动化了上传页面到服务器的操作。批处理文件如下:

@echo off
rem pagesync.bat - update site pages to server
python makesite.py
psftp -b pagesyncbatch.txt [email protected]
echo Page sync done.
pause

第一行 @echo off 基本上时 Windows 下批处理文件标准开头。它的意思就是关闭对命令的 echo,这样执行批处理文件时不会将里面的命令都再显示一次。第二行是注释行,以 rem 开头的行都被看做注释。3-4 行就是我需要用来更新页面的命令,它们按顺序执行。倒数第二行的 echo 其实也是一个 Windows 下的命令,它会将它的参数都输出到屏幕上;最后一行的 pause 被执行的时候我们就会看到我们熟悉的按任意键继续,如果你是在资源浏览器里执行的这个批处理文件,pause 会保持当前窗口在执行完后不会立即关闭,好让你再观察命令行输出的结果。

显然批处理还有条件判断等很多高级的功能,如果你需要到更复杂的操作的话,建议你自己在网上搜下 Windows 批处理文件的文档。不过其实很多情况只需要顺序的执行几个命令,那么上面这些看下来应该就足够啦。

版本控制

计算机发展了这么多年,开发软件的方式也在不断进化。除了我们使用的编程语言,工具在不断的更新,软件开发的方法论逐渐发展形成为软件工程(Software Engineering)。如敏捷开发,测试驱动开发,规格驱动开发,结队编程这些崭新的方法层出不穷,使用这些科学的方法能规范化软件开发的过程,降低成本提高效率,让软件开发这个混沌的过程变得可以控制,管理。

上面这瞎掰的一段看起来很棒吧!事实上软件工程的发展就我们普通开发者来看,说实话影响不太大。但版本控制(Revision Control)绝对是一个对任何一位常写代码的人都会产生影响的概念。如果你在完全么有听说过版本控制,SVN,Perforce这些东西的话,那么希望通过下面的内容帮助你了解版本控制是帮助你简化很多你常遇到,但找不到简单处理方法的问题。接下来的例子中,我们会使用 Mercuial 这个近些年流行起来的版本控制软件,来管理我们的代码,更棒是我们会将这个代码放到 BitBucket 这个网站上,这样你一方面你可以将你的代码做一个备份,放在网站上也能方便的查看,同时你也控制是否让所有人都看到你的项目,还是只让自己能看到。

准备

首先我们要安装 Mercuial,这就是我们要使用的版本控制软件。点击这里访问他们的网站,点击右边大大的 Download 按钮,将下下来的安装程序装上就可以了。接下来启动一个命令行窗口,输入 hg ,如果你看到 Mercuial 正常运行并显示 分布式软件配置管理工具 - 水银 以及一大堆英文帮助的话,那就算装好了。hg 是 Mercuial 主程序的名字,所以经常也用 hg 来称呼 Mercuial,下文中我也使用 hg 这个亲切的名字。

同时你应该能在开始菜单里找到一个新的项目 TortoiseHg, 我们打开其中的 TortoiseHg Workbench。这就是 Mercurial 自带的一个图形界面。如果你觉得不知道点哪里,那就对了。事实上这个 GUI 并不是很好用,接下来的例子中我们也是主要使用命令行下的 hg 来进行操作。但我们首先要使用它来进行一些设置。选择菜单中的 File - Settings,在这里你可以设置 hg 的全局设置。这里选到 Commit,右边第一项是 Username,这个是很明显的,就是我们在 hg 中使用的名字。这里请将其设置为 first last <[email protected]> 这样的格式,前面是可以是带空格的名字,后面必须用空格隔开并带上 <> 包括起来的邮箱。你需要记住你的这个邮箱,因为后面的例子中我们还是要用到它的!

设置TortoiseHg

如果你像我一样不愿意安装 TortoiseHg 的话,那么可以手动设置一下 Mecurial.ini 文件。对于 Win7 用户它位于 C:\Users\<你的用户名> 目录下。如果没有的话新建一个 Mercurial.ini 就可以了。内容请填上:

[ui]
editor = Notepad
username = your_name <your_email_address>

这样我们就设置好了 hg 全局用户名和基本编辑器,后面就可以开始用了。

简单的使用

现在我们开始使用 hg 来进行版本控制。在你常用的目录建立一个新的文件夹,将其命名为 lnecho,这也是我们之前例子的程序的名字。在命令行下 cd 到这个目录中,输入 hg init 来建立我们的第一个代码仓库(repository):

C:\TEMP\lnecho>hg init

C:\TEMP\lnecho>dir
 驱动器 C 中的卷是 BOOTCAMP
 卷的序列号是 3883-279A

 C:\TEMP\lnecho 的目录

2011/09/25  21:50    <DIR>          .
2011/09/25  21:50    <DIR>          ..
2011/09/25  21:50    <DIR>          .hg
               0 个文件              0 字节
               3 个目录 22,077,804,544 可用字节

hg init 命令后,我们发现在这个空的文件夹中多出了一个名为 .hg 的目录。这就是 hg 用来存放他内部管理数据的地方。一般我们不用对他进行操作,但一般只要在一个目录中有一个 .hg 的文件夹,那么此文件夹就是一个 hg 的 代码仓库(repository),也就是 hg 管理项目的基本单位,此文件夹下的所有文件都属于此仓库。

到这里我们的准备工作就做完了,现在我们可以正式开始使用 Hg 。你可以打开一个编辑器,把上面例子中的 lnecho 程序保存下来,存为 main.c,现在我们就可以把这个程序放入 Hg 的仓库中,通过 Hg 来对它进行管理。在上面的操作之后,我们输入如下命令:

C:\TEMP\lnecho>hg add main.c

C:\TEMP\lnecho>hg status
A main.c

这里我们看到了两个以 hg 开始的命令,addstatus。这也是 Hg 使用的方式,我们运行 hg 程序,将我们想要的操作,例如 add, init 和他们具体的设定,以参数的形式传递给 hg。如果你不太知道这里发生了什么的话,说明你上一章估计么看懂(也有可能是我写的不够清楚...)。

总而言之,我们总是以这种方式来使用 Hg, 虽然运行的是同一个程序,但它实现的具体功能都是由 hg 后面的命令决定的。上面的 init,代表初始化,将当前目录初始化为一个仓库。add 是将你后面指定的文件加入到这个仓库中。注意,虽然我们现在这个文件夹已经是一个代码仓库,但是 Hg 要求你显式的确认仓库目录中你需要管理的代码文件。很明显,这样做的一个好处就是你可以在这个目录中任意放置各种临时文件,而 Hg 只会关注你希望它管理的代码文件。接下来的 status 命令,是显式你当前仓库中所管理的文件的状态。这里的显示 A main.c ,其中 A 代表 added,表示 main.c 已经被添加到我们的仓库中了。同时,你可以使用 help 来查询每一个命令的具体功能和使用方法,例如 hg help status

版本

既然 Hg 是版本控制软件,那么版本显然是它其中非常重要的一个部分。我们可以通过 hg log 来查看当前仓库中的所有版本:

C:\TEMP\lnecho>hg log

如果上面的步骤你都正确的做完了的话,你会发现输入 hg log 后没有任何结果。这正反映了我们这个仓库中的状态,我们还没有创建任何一个版本。“版本控制软件”里开头就是版本这俩字,显然版本是 Hg 中一个重要的组成部分。事实上,你可以理解版本代表着你的代码在某一时间上的状态,而 Hg 能在你的指示下将这个状态永久的保存起来,之后你可以查看相对于这个状态做出的改动,或者让你的代码回到任意一个版本的状态上,还有更多高级的功能。那么现在我们将创建 lnecho 的第一个版本:

C:\TEMP\lnecho>hg commit -m "Initial Commit."

C:\TEMP\lnecho>hg log
修改集:      0:4a0806dc10ae
标签:        tip
用户:        jagt <[email protected]>
日期:        Mon Oct 10 20:34:56 2011 +0800
摘要:        Initial Commit.

commit 就是 Hg 中用于创建版本的命令。它字面上的意义是“提交”,你可以理解为你仓库中的代码已经到了某个令人兴奋状态,比如实现了一个新的功能,改正了一个bug,这时我们得赶紧把代码保存一下,而 commit 命令就能让你将当前代码库中需要管理的文件(就是你之前 add 过的)当前的状态永久的储存起来,形成一个版本。后面 -m 参数后的字串是你对当前版本的描述。现在我们再输入 log, 我们可以看到已经出现了我们刚刚提交的那一个版本。

意外情况

编写代码的时候大家往往都会犯些很愚蠢的错误。这里我升级了一下之前的程序,现在让他在结束前输出总参数的个数,代码如下:

#include <stdio.h>
/* lnecho : echo arguments in lines */

int main(int argc,char* argv[]) {
    int ix;
    for(ix = 0; ix < argc; ++ix) {
        printf("%s\n", argv[ix]);
    }
    printf("total %d arguments\n", argc);
    return 0;
}

你简单的对比一下,你应该看到我只加了一行新的 printf,你可以试试将这段代码复制粘贴到你之前的 main.c 中代替原来的代码,再按上面的方法编译试试看。结果编译不通过了!

C:\TEMP\lnecho>cl /Felnecho.exe main.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

main.c
main.c : warning C4819: The file contains a character that cannot be represented
 in the current code page (936). Save the file in Unicode format to prevent data
 loss
main.c(4) : error C2143: syntax error : missing ')' before '*'
main.c(4) : error C2143: syntax error : missing '{' before '*'
main.c(4) : error C2059: syntax error : ')'
main.c(4) : error C2054: expected '(' to follow 'argv'

C:\TEMP\lnecho>

呃,这下糟糕了,你能迅速从错误信息中看出问题在哪里么? 先不管你结果如何,这里有个好消息:使用 Hg 后我们就不需要辛苦的找寻到底是哪里可能出了问题,Hg 能迅速的告诉我们有嫌疑的地方:

C:\TEMP\lnecho>hg diff
diff -r 4a0806dc10ae main.c
--- a/main.c    Mon Oct 10 20:34:56 2011 +0800
+++ b/main.c    Mon Oct 10 20:53:14 2011 +0800
@@ -1,10 +1,11 @@
 #include <stdio.h>
 /* lnecho : echo arguments in lines */

-int main(int argc, char* argv[]) {
+int main(int argc锛宑har* argv[]) {
     int ix;
     for(ix = 0; ix < argc; ++ix) {
         printf("%s\n", argv[ix]);
     }
+    printf("total %d arguments\n", argc);
     return 0;
 }

diff 命令在这里的作用,是显示出当前状态(注意,是还没有 commit 提交的状态)与仓库中最后一个版本之间的差别。换句话来说,就是显示出我们在提交了上个版本之后,做了哪些改动。这里的显示结果是一个标准的 Linux 格式的 diff 结果,其中头几行显示的是被比较的文件名,如果有多个文件的话那么会有多个类似的行。后面你可以看出来,就是我们 main.c 里的内容。这里 - 开始的行是被去掉的行,而 + 开头的行就是当前被添加的行。上面 main 的那里连续一个 - 一个 +,其实就是这行已经被修改过了。我们修改过的结果中出现的那两个乱码相当可疑,你在 main.c 里仔细看会发现那是一个中文全角的逗号,显然这不是 C 标准中的一部分。现在,在 Hg 的帮助下我们很容易的找到了问题的原因。

这时候我觉得这个错误实在是太丢脸了,我想放弃当前的改动,回到之前那个版本的状态。显然 Hg 也能帮助你轻松做到这一点:

C:\TEMP\lnecho>hg revert --all
正在恢复 main.c

通过 revert 命令,我们放弃了当前未提交的所有更改,回到了之前那个版本的状态。事实上,使用了版本后,你在进一步开发的时候就不需要那么小心翼翼,因为你随时都可以轻松回到之前的一个状态。这也避免了你到处拷贝代码的备份这种无奈的情况。Hg 有一个很贴心的功能是他会将你放弃的这些修改以 .orig 为后缀名保存在目录下,比如在上面操作结束后就会生成一个 .orig 文件,这防止了很多情况你不小心 revert 而损失了工作结果的局面。事实上,revert --all 并不是最好的利用之前版本的方式,当你熟悉了 Hg 以后你可以试着学习使用 update 来更为保险的完成这个操作。

到这里,我对 Hg 的基本介绍就结束了。如果哪位读者熟悉 Hg 的话肯定会立马掀桌,“讲 Hg 居然没有提到 branch 和 merge,坑爹啊!” 好吧,但这篇文章显然不可能介绍清楚 Hg 的方方面面,上面的例子应该足够让新手在没有多人协作的情况下开始使用 Hg 了。而事实上 branch 和 merge 更是版本控制以及 Hg, Git 的精髓功能。如果你已经上手开始使用 Hg,那么这里有两本非常棒的书帮助你进一步了解 Hg:

  • Hg Init 又是一本带插画的教程,非常清楚的使用 Hg 的方方面面。

  • Mercurial: The Definitive Guide 这本可以当做参考,当你有哪些命令不太清楚,或者对某个高级功能有想法的时候,不妨在这里看看吧。

Teh INTERNET!!

那么激动人心的时刻到来了,这里我们要将我们之前的建立好的 Hg 代码仓库传到网上去。事实上,使用版本控制的另一个好处就是你可以方便的使用它自带的功能来进行代码部署和备份。大部分版本控制软件的概念中,都有一个中心的仓库作为整个项目的中心。接下来的内容里,我们会在 BitBucket 上建立一个远程的仓库,我们可以再本地进行开发和修改,再将本地的修改同步到远程服务器上。这样一方面你可以有规范的开发流程,即使单人做开发也能有很专业的感觉;另一方面 BitBucket 这类的网站都会为你的项目提供一个很方面的可视化管理环境,比起你本地装的 GUI 效果会好很多。而且现在最潮的不就是什么都在浏览器里弄么。

首先,用你最喜欢的浏览器打开 BitBucket 的网站,点击页面上那个大大的 Sign Up 按钮,然后按照常用流程注册好你的账号就行了。这里要注意的是你选的用户名和邮箱最好跟我们之前在 TortoiseHg 里设置的 username <[email protected]> 相匹配,这样你在你项目里你的头像和信息才能正常显示出来。

之后登陆后我们就要准备建立我们的第一个仓库了。如下图中,点击 Repositories 右边的加号。

建立仓库

之后就是建立仓库的具体页面,下图为我们 lnecho 程序的设置,几个基本选项如名字和信息这些大家看看就知道,这里比较要紧的是右边的 private 选项,这里可以控制这个仓库是不是只有你自己能访问,这里没有勾上这个选项,那么这个创建出来的项目就会是任何人都可以访问的。另外左边的 Repository Type 可以选择你要使用的版本控制软件,不久前 BitBucket 宣布支持 Git 于是这里有了这个选项,我们现在不用管它,按默认选择 Mercuial 就行了。最后点击最下面的 Create repository 我们的项目页面就创建好啦。这里我在我的账户下创建了这个项目,你可以访问这里作为参考。其实你也可以理解为这是你整个项目的一个主页,从这里可以可视化的查看你的代码仓库。

建立仓库设置

接下来,我们先要将我们上面的 lnecho 项目传到这个项目页面。下图是项目页面的一段简洁,除了之前我们设置好的信息以外,红框内的内容是我们需要关心的。这里的 https://[email protected]/jagt/lnecho 其实就是一个远程的 Hg 代码仓库的地址。它和你本地的代码仓库可以看做是一样的,好比我们的 lnecho 是在 C:\Temp\lnecho 目录下,那对我我们这个本地的仓库他的地址就是 C:\Temp\lnecho。而 BitBucket 上的这个仓库,我们可以认为他跟一般的仓库么有区别,只是地址是一个远程的地址而已。

仓库地址

现在,我们通过 push 命令来将本地仓库推到远程去。

C:\TEMP\lnecho>hg push https://[email protected]/jagt/lnecho
pushing to https://[email protected]/jagt/lnecho
searching for changes
http authorization required
realm: Bitbucket.org HTTP
user: jagt
password:
remote: adding changesets
remote: adding manifests
remote: adding file changes
remote: added 1 changesets with 1 changes to 1 files
remote: bb/acl: jagt is allowed. accepted payload.

push 期间你需要输一次密码。如果你得到的结果跟上面这段差不多,那么你再次访问你的项目页面你就应该看的到你的代码了。到这里,我们就成功的把我们的代码放到了网上。之后的开发中,你每次使用 push 命令就能将你本地的版本上传到远程。

总结

说实话,以上关于 Hg 的介绍很多方面的描述都不是很准确,对 Hg 里版本的概念,push, commit 具体功能讲的也不是很准确。但这节短短的几段的目的,就是希望能让完全没有版本控制软件经验的同学能迅速上手 Hg。这之后如果你继续使用 Hg,随着你使用到它更多的功能,你必然也会遇到各种问题。这时候你就只能靠自己阅读 Hg 的文档和相关在线书籍啦。

最后

本文到这里就结束啦。最后再介绍一个独家小窍门,如果你在计算机相关的日常工作里,碰到了某些让人恼怒的小问题,或者某些情况需要频繁重复操作,这时候你不妨试试在网上搜搜看。往往等你遇到这些问题的时候,不知道已经有多少人已经碰到过这些问题了,所有常常都会有些现成的方法来搞定你的困扰。以上,希望这些能让你的学习工作生活更开心~

To the extent possible under law, the person who associated CC0 with this work has waived all copyright and related or neighboring rights to this work.

home | top