A.1 命令行

在图形用户界面(Graphical User Interface,GUI)日益发达的今天,命令行使用得越来越少。但笔者仍然认为命令行操作是每一位编程竞赛的选手必须掌握的技能。它不仅可以让你看起来很专业,而且确实能帮你很大的忙。

首先,进入命令行。在Windows XP中,可以选择“开始”菜单中的“运行”命令,在弹出的“运行”对话框中输入“cmd”,然后按Enter键,将出现类似下面的提示信息:

Microsoft Windows XP [版本 5.1.2600]

(C) 版权所有 1985-2001 Microsoft Corp.

C:\Documents and Settings\Administrator>

其中,C:\Documents and Settings\Administrator是当前路径,而后的“>”符号是命令提示符,紧跟其后的是闪烁的光标(cursor)。在文本界面中,所输入的任何信息都将出现在光标的所在位置。输入命令之后不要忘记按Enter键。

在Linux中,打开终端(terminal)即可进行命令行操作。Linux终端并不一定会显示当前路径,可以用pwd命令将其显示。无论是Windows还是Linux,都可以用上下箭头来翻阅并使用历史记录。Windows和Linux下都可以用Tab键补全命令,但在细节上存在一些差异,读者可以自己实践或查阅相关资料。

A.1.1 文件系统

学习命令行的第一步是理解文件系统。相信读者对“文件”这一概念已经有所认识,但除此之外还需要清楚文件所在的位置。“位置”的表达方式有两种,一种是相对路径,另一种是绝对路径。

相对路径(relative path)是相对当前路径(current path)而言的,它在命令行中已有所体现。例如,在上面的例子中,当前路径是C:\Documents and Settings\Administrator。在这种情况下,命令type abc.txt即为试图显示C:\Documents and Settings\Administrator\abc.txt。

除了直接给出文件名外,还可以借助当前目录“.”和父目录“..”进行更为灵活的相对路径引用。例如,在上面的命令行提示符下输入type..\..\Windows\123.txt,实际上是在试图显示c:\Windows\123.txt。

在命令行中可以用“cd <目录名>”的方式改变当前路径。例如,“cd..”会进入父目录,而“cd aaa”会进入当前目录的aaa子目录。

绝对路径和相对路径的区别是,前者给出了“起点”,其实际指向不随当前路径变化。在算法竞赛中,不要在提交的源代码中引用绝对路径,但在操作和调试程序的过程中可以随意使用绝对路径。另外,Linux中的路径分隔符是正斜线“/”,而非反斜线“\”。

如果在程序中读写文件,则当前路径一般和该程序位于同一个目录,但也可以更改。如果在执行程序时出现“找不到文件”的错误,而文件确实存在,则极有可能是程序的“当前路径”与所想的不一致。一个笨(但有效)的方法是用freopen("test.txt","w",stdout)的方法创建文件test.txt。找到了这个文件,就知道当前路径是什么了。如果要在freopen或者fopen中使用“..\..\Windows\123.txt”这样的相对路径,应注意反斜线字符在C语言的正确表示方法是“\\”。不过,即使在调试中也尽量不要使用路径名。如果在提交程序前忘记把路径名删除,将导致程序得0分。事实上,这样的例子并不少见。当然,如果只在条件编译中使用路径名,则是没有问题的。

最后一个小问题是:你不一定有存取文件的权限。如果出现类似于“Permission Denied”的错误信息,需确认当前用户是否拥有想访问的目录或者文件的访问/修改权。在现场比赛中,这可能是因为没有使用比赛指定账户,而是改用guest登录了。

A.1.2 进程

简单地说,进程是一个程序正在执行时的实体。它消耗CPU资源且占用内存。进程一般都有名字,同时还有一个编号(称为PID)。

在Windows和Linux中都能方便地列出进程。在Windows下可以使用Ctrl+Alt+Del组合键打开任务管理器,或者在命令行下用tasklist命令。在Linux下可以用top命令查看当前占用CPU资源最多的一些进程,而ps命令类似于Windows下的tasklist命令,它是使用列表的方式给出当前进程。在默认情况下,ps命令并不会列出系统进程,用ps ax命令可以列出更多的进程。

强行终止进程有很多方法。在Windows下,可以用任务管理器直接终止,也可以在命令行下用taskkill/pid <PID>或taskkill /im <映像名>终止进程,可以通过执行taskkill/?查看更多选项。

在Linux下可以用kill命令终止命令,还可以用killall <进程名>命令把某个进程名对应的所有进程终止。一个典型情况是,如果pascal选手的Lazarus IDE不响应,就可以用killall lazarus把它们终止。

作为一个好习惯,当程序非正常终止,或者系统表现异常时,应检查进程。例如,若系统反应特别慢,可能是有一些看似运行结束,但其实残留在系统中继续占用系统资源的进程。

A.1.3 程序的执行

在命令行下执行一个程序比在IDE中执行要方便和灵活得多。基本的方法很简单:只需直接输入程序名即可。

例如,在Windows下执行abc.exe,可以进入它所在目录后直接输入abc并按Enter键。系统为什么能找到abc.exe呢,因为在Windows下,当前目录是最先搜寻可执行文件的位置,并且扩展名.exe在搜索之前会被自动添加。如果当前目录没有abc.exe,是否会报错呢?不一定。运行path命令,会看到一连串目录。如果当前目录没有abc.exe,系统会继续在这些目录中寻找,全部查找完毕仍没找到时才会报错。在搜索文件时并不会检查上述目录下的子目录。

Linux有一些不同。首先,它的可执行文件名并不是以“.exe”为扩展名的,因此g++ abc.cpp -o abc编译出的文件是abc,而非abc.exe(当然,如果一定要将其取名为abc.exe,也无不可)。另外,当前目录并不在搜索路径中,因此,即使abc已经在当前目录中,仍需要用./abc这样的方式告诉Linux“可执行文件abc就在当前目录”。

A.1.4 重定向和管道

很多比赛要求选手直接读写标准输入输出(即用printf/scanf或cin/cout读写,且不用freopen),难道在评分时裁判要将输入数据一一用键盘输入,等程序运行结束之后看着屏幕,逐个对照手中的标准答案吗?当然不是。可以使用重定向的技巧将输入文件塞到程序的标准输入中,然后再将程序输出保存在文件中。

在Windows下可以使用abc < abc.in > abc.out。而在Linux下则可以使用./abc < abc.in > abc.out。当然,如果可执行文件和输入输出文件不在同一个目录,则需要进行相应调整。但基本方法是不变的:在输入文件名前面加一个“<”符号,而在输出文件名前面加一个“>”符号。注意,此时的输出文件将被覆盖。如果希望只是把输出附加在文件末尾,则可用“>>”代替“>”。此外,如果有大量的文本输出到标准错误输出,还可以用“2>”将它们重定向,但需注意,尽量不要在正式提交的程序中输出到标准错误输出,这样不仅可能会违反比赛规定,还可能会因为大量文本的输出而占用宝贵的CPU资源,甚至导致超时。

Windows和Linux均提供“管道”机制,用于把不同的程序串起来。例如,如果有一个程序aplusb从标准输入读取两个整数ab,计算并输出a+b,还有一个程序sqr从标准输入读取一个整数a,计算并输出a2,则可以这样计算(10+20)2: echo 10 20 | aplusb | sqr。尽管也可以用重定向来完成这个任务,但用管道明显要简单得多。

另一个常见用法是分页显示一个文本文件的内容。在Windows下可以用type abc.txt | more,在Linux下则是用cat abc.txt | more。

A.1.5 常见命令

在Linux中,可以用time命令计时。例如,运行time ./abc会执行abc并输出运行时间。但Windows中并没有这样的命令,幸好在大多数情况下只是在对自己编写的程序计时,因此只需在程序的最后打印出clock()/(double)CLOCKS_PER_SEC即可(需要包含time.h)。

附表A-1中给出了一些常见命令的Linux版本和Windows版本,供读者查阅。

附表A-1 常见的Linux命令和Windows命令

 

  分类     Linux命令     Windows命令  
  文件列表     ls     dir  
  改变/创建/删除目录     cd/mkdir/rmdir     cd/md/rd  
  显示文件内容     cat/more     type/more  
  比较文件内容     diff     fc  
  修改文件属性     chmod     attrib  
  复制文件     cp     copy/xcopy  
  删除文件     rm     del  
  文件改名     mv     ren  
  回显     echo     echo  
  关闭命令行     exit     exit  
  在文件中查找字符串     grep     find  
  查看/修改环境变量     set     set  
  帮助     man <命令>     help <命令>