掌握VI编辑器

Standard

原文:http://www.daxixiong.com/?/article/10

资料来源:University of Hawaii at Manoa College of Engineering。

引言
VI编辑器是一个被许多Unix用户使用的基于屏幕的编辑器。VI编辑器具备强大的功能来帮助程序员,但是许多初学者因为要面对很多不同的指令而对使用VI敬而远之。写作本教程的目的就是要帮助初学者适应VI编辑器的使用,当然,也有一些小节的内容与VI的惯用者有关。在讲解的同时举出了很多的例子,最佳的学习方法就是试着在Unix下运行这些例子,并试着举一反三。在这个世界上,没有比自己亲自去经历更好的学习方式了。

约定
在本教程中,使用以下的约定:
^X表示一个控制字符。例如,如果你在教程中看到:^d,其意是你按下了ctrl键,然后敲入了相应的字母。对于本例来说,你就按下ctrl键,然后敲d。

开始之前
VI编辑器使用全屏幕,因此了解你使用的是何种类型的终端是有必要的。当你登录的时候,wiliki会问你的终端是什么。提示信息像这个样子:TERM = (vt100)。
如果你知道自己的终端是一个vt100(或者一个能够当作vt100用的模拟器),在你登录的时候,就为终端类型敲击回车键。如果你有一个hp终端,为终端类型输入“hp”并回车。如果你不确定自己的终端类型,问一个实验室的管理员,或者是请别人帮助你设置正确的终端类型。
如果在登录的时候,你犯了一个错误,输入了错误的终端类型,不要紧张,退出就行了。你可以输入以下的命令来修补设置:
首先,告诉你的shell你的终端是何种类型。(如果你不确定你的shell是什么,可以输入这个命令:echo $SHELL)。对于上面已经给出的例子,终端类型是“vt100”。用你拥有的任何终端类型去替换它。对于C shell(/bin/csh),命令是这个:set term=vt100。对于Bourne Shell(/bin/sh)或者是Korn Shell(/bin/ksh),命令如下:export TERM   TERM=vt100。下一步,用这个命令重设你的终端:tset。
现在,终端的类型被正确设置(希望如此吧),你已经准备好来开始使用VI了。

启动VI编辑器
VI编辑器允许用户生成新的文件或编辑已存在的文件。启动VI编辑器的命令是:vi,紧接着是文件名。例如,为了要编辑一个叫做temporary的文件,你要输入“vi temporary”并回车。你也可以不用文件名来启动vi,但是当你想保存自己的工作的时候,你必须要告诉VI将要把这些内容保存到哪个文件中。
当你第一次启动VI的时候,你会看到在屏幕的左边充满了波浪号(像“~”)。在文件结束之后的任何空行都是如此显示。在屏幕的底部,文件名被显示出来。如果专门指定了一个已经存在的文件,文件的大小也会被显示出来,就像这样:”filename” 21 lines, 385 characters。
如果你指定的文件不存在,系统会告诉你这是一个新文件,就像这样:”newfile” [New file]。
如果你不要文件名而启动VI,当VI启动的时候,屏幕的底部会是空白。如果屏幕没有显示这些预期的结果,你的终端类型可能被错误地设置了。输入:q并回车来退出VI,并且修补你的终端类型。如果你不知道怎么做,问一下实验室管理员。

退出VI
你已经知道了如何进入VI,了解一下如何退出它也是很好的。VI编辑器有两种模式,为了退出VI,必须要处于命名(command)模式。敲击“Escape”或“Esc”键(如果你的终端没有这个键,可以试试^[,或control-[)来进入命名模式。如果在你敲击“Escape”的时候,你已经在命令模式之下,不要担心。系统会发出警告,但是你仍然处于命名模式。
离开VI的命令是:q。当处于命名模式之下,输入冒号以及“q”,并回车。如果你的文件被修改过了,编辑器会警告你,同时也不让你退出。为了忽略此消息,不保存就退出VI的命令是:q!。它让你不用保存任何改动而退出VI。
当然,一般说来,在一个编辑器中,你还是想保存你做出的改变。保存编辑器内容的命令是:w。你可以将以上命令和退出命令结合起来,即:wq。你可以指定一个不同的文件名来保存这些内容,这是通过在:w之后指定文件名来实现的。例如,你想将你正在编辑的文件保存为另外一个叫做filename2的文件名,你可以输入: w filename2并回车。
另外一种保存你的改变并退出VI的方法是ZZ命令。在命令模式下,输入ZZ,它会做与:wq相同的事情。如果文件的内容有任何改变,这些改变会被保存下来。这是离开编辑器的最容易的方法,只需要敲击键盘两次。

VI的两种模式
大多数用户了解VI编辑器的第一件事就是它有两种模式:command(命令)和insert(插入)。command模式允许命令条目来操纵文字。这些命令通常是一个或两个字符长,可以敲几下键盘便被输入了。insert模式将任何在键盘上敲击的内容输入现在的文件中。
VI是以command模式启动的。有几个命令来将VI编辑器转入insert模式。最常用的命令是a和i。这两个命令在前面已经描述过了。当你在insert模式下时,敲击退出(Escape)按钮,你就退出此模式了。如果你的终端没有这个键, ^[或control-[也行。你可以快速敲两下退出(Escape)按钮,这时VI肯定会在command模式之下的。当你已经在command模式之下时敲击退出(Escape)按钮并不会让编辑器退出command模式。系统会提醒你已经在此模式下了。

如何在命令模式下输入命令
命令(command)模式下的命令的格式一般是这样(括号中是可选的参数):[count] command [where]。
大多数命令只有一个字符长,包括那些使用控制字符的命令。本节中描述的命令是在VI编辑器中最经常使用的。
count可以是1到9之间的任何一个。例如,x命令删除在光标之下的那个字符。如果你在命令模式下输入23x,会有23个字符被删除。
一些命令使用一个可选的where参数,你可以指定命令影响到多少行或者是文件的多少部分。where参数也能够是任何移动光标的命令。

一些简单的VI命令
以下是一个简单的命令集合,它们足以让初学者起航。也有许多其它的方便的命令,这将在后续章节中讨论。

  • a:进入插入(insert)模式,输入的字符会被插入到当前光标位置之后。如果你指定了数目(count),插入的所有文字会被重复那么多次。
  • h:向左把光标移动一个字符的位置。
  • i:进入插入(insert)模式,输入的字符会被插入到当前光标位置之前。如果你指定了数目(count),插入的所有文字会被重复那么多次。
  • j:将光标向下移动一行。
  • k:将光标向上移动一行。
  • l:向右把光标移动一个字符的位置。
  • r:将光标所在位置的字符替换掉。指定数目(count)来替换许多字符。
  • u:撤销对文件所作的最后一次修改。再一次输入u会恢复最后一次修改。
  • x:删除光标所在位置的字符。count指出了要删除多少字符。光标之后的字符会被删除掉。

VI中的文字缓存
VI编辑器有36个缓存来存储文字片,同时也有一个通用目的缓存(general purpose buffer)。任何时候,在文件中,当一个文字块被删除或整形,它被放入通用目的缓存中。VI的大多数用户很少使用其它缓存,因此在没有其它缓存的情况下也能够活得很快活。如果被指定的话,文字块也能够被存储在其它缓存中。用”命令来指定缓存。在输入”之后,必须要输入指明缓存的字母或数字。例如,命令:”mdd使用了缓存m,最后的两个字符代表删除当前行。类似地,可以使用p或P命令来粘贴文字。”mp在当前光标位置之后粘贴缓存m的内容。对于之后两节所使用的任何命令,这些缓存被指定用于文字或段落的临时存储。

剪切与整形
用于剪切的常用命令是d。此命令从文件中删除文字。在这个命令之前是一个可选的count,之后是一个移动说明。如果你输入dd,会删除当前行。以下是这些命令的一些组合:

  • d^:删除从行首到当前光标所在位置的内容。
  • d$:删除从当前光标所在位置到行末的内容。
  • dw:删除从当前光标所在位置到字末的内容。
  • 3dd:从当前光标所在位置向下删除3行。

与d命令的功能类似,y命令从文件中提取文字而不删除文字。

粘贴
粘贴的命令是p或P。它们的区别仅在于当粘贴的时候相对于光标的位置。p在当前光标之后粘贴专门的或一般的缓存,而P则在当前光标之前粘贴专门的或一般的缓存。在粘贴命令之前指定数目(count)会将文字粘贴数次。

缩进代码与检查
VI编辑器有功能来帮助程序员将它们的代码布局得更加的整洁。有一个变量来为代码中的各级嵌套设定缩进。为了使用这个功能,可以阅读本教程的“customization section”一节。例如,将偏移宽度设为4个字符的命令是:set sw=4。
以下的命令缩进你的代码或移除缩进,同时也能够用count来指定:

  • <<:将当前行向左移动一个偏移宽度。
  • >>:将当前行向右移动一个偏移宽度。

VI编辑器也有一个有用的功能来帮助你在遇到悬挂圆括号或大括号的时候检查你的源代码。%命令会寻找与一个特别的右括号相对应的左括号,或与之相反。将光标放到一个括号上并敲击%来将光标移动到相应的括号。这个功能对于检查未闭合的括号是很有用的。如果有一个不匹配的括号存在,VI会发出嘟嘟声,这是在提示你没有发现配对的符号。

文字与字符搜索
VI编辑器有两类搜索:字符串和字符。对于一个字符串搜索,使用/和?命令。当你开始使用这个命令的时候,在最底部一行会显示你敲入的命令,在命令后面可以输入你想要搜索的特殊字符串。这两个命令仅在搜索发生的方向上有区别。在文件中,/命令向前(向下)搜索,?命令向后(向上)搜索。n和N命令分别在相同或相反的方向上重复之前的搜索命令。一些字符对于VI来说,有特殊的意义,因此在它们前面必须要放置一条斜线(\)来被当作搜索表达式的一部分。
特殊字符:

  • ^:行的开始(一个搜索表达式的开始)。
  • .:匹配一个单字符。
  • *:匹配0个或多个之前的字符。
  • $:行的结束(一个搜索表达式的结束)。
  • [:开始一系列匹配,或者是非匹配的表达式。例如:/f[iae]t匹配三者之一:fit fat fet。在这种形式下,它不会匹配这些:/a[^bcd]不会匹配任何字符串,除了带一个a和另外一个字母:ab ac ad。
  • <:将之放在以反斜线结束的表达式中来寻找一个字的结束或开始。例如,/\<the\>只会发现the,而不是这些字:there和other。
  • >:参考对于“<”的描述。

字符搜索在一行之内搜索来寻找在命令之后输入的一个字符。f和F命令只在当前行上搜索一个字符。f向前搜索,而F向后搜索,同时,光标会移动到所发现字符的位置。
t和T命令只在当前行上搜索一个字符,对于t来说,光标移动到字符之前的位置,而T向后搜索行到字符之后的位置。
这两套命令使用;和,命令来重复,其中;在相同方向上重复上一条字符搜索命令,而,在相反方向上重复上一条字符搜索命令。

VI(以及EX)的设置
你能够在启动的时候个性化(customization)VI的行为。有几个编辑选项使用:set命令,以下是在Wiliki上的VI和EX编辑器选项(你可以在命令模式下通过输入:set all并回车来获得此列表):

有一些选项具有用等号“=”设置的值,而其它选项有的有,有的没有。(这些开关类型叫做Boolean,在它们的前面有“no”来暗示它们不是设置的。)在此展示的选项是没有进行个性化设置的选项。下面用缩写给出了这些选项的描述。例如,命令设置自动缩进,可以输入:set autoindent或set ai。为了去除设定,你可以输入:set noautoindent或set noai。

  • autoindent (ai):此选项对编辑器进行设置以使得在一个缩进行之后的行像前面行那样缩进。如果你想back over此缩进,可以在第一个字符的位置输入^D。^D工作在插入(insert)模式,并不在命令(command)模式。当然,可以用shiftwidth来设置缩进的宽度,下面有解释。
  • exrc:在启动的过程中,会读入当前目录下的.exrc文件。这可以在环境变量EXINIT或你的主目录下的.exrc文件中设置。
  • mesg:如果对选项解除设置,要关闭消息,使用:set nomesg,这样做以使得当你在使用编辑器的时候没有人能够打扰你。
  • number (nu):用在左边的行号来显示行。
  • shiftwidth (sw):此选项带有一个值,用此值来定义一个软件制表位(tabstop)的宽度。(这个软件制表位用于<<和>>命令。)例如,你可以用此命令来设置偏移宽度为4:set sw=4。
  • showmode(smd):此选项用于显示你所用的编辑器的实际模式。如果你在插入(insert)模式下,屏幕的最底下一行会显示INPUT MODE。
  • warn:如果你修改了文件,但是没有保存,该选项会警告你。
  • window(wi):该选项设定VI使用的屏幕上的行数。例如,要设定VI编辑器只使用你的屏幕的12行(因为你的modem很慢),你可以使用这个:set wi=12。
  • wrapscan(ws):此选项会影响到文字搜索的行为。如果wrapscan被设置了,要是没有在文件的底部找到要寻找的文字,它会试着在开始部分寻找它。
  • wrapmargin(wm):如果此选项有大于0的值,编辑器会自动“word wrap”。也就是说,如果你占用左边部分太多的空间,文字会转向下一行而不用敲回车。例如,要设定wrap边界为2个字符,可以输入:set wm=2。

写和将密钥(关键字)映射到其它密钥(关键字)
一个在VI编辑器中有用的EX编辑器命令是abbreviate命令。它让你为特殊的字符串设定缩写。此命令像这样:ab string thing to substitute for。例如,如果要敲入名字“Humuhumunukunukuapua`a”,但是你不想敲入整个名字,那么你可以使用缩写。在此例中,像这样敲入命令:ab 9u Humuhumunukunukuapua`a。
现在,当你单独敲入9u的时候,VI会敲入它所代表的整个字。如果你敲入了9university,它就不会替换这个字。
去除之前定义的缩写的命令是unabbreviate。例如,去除之前例子的命令就是”:una 9u”。如果你要获取缩写列表,只需要简单地输入:ab,而不用任何定义。
另外一个对于个性化很有帮助的EX编辑器命令就是映射(mapping)命令。有两类映射(mapping)命令。一个用于命令模式,另一个用于插入模式。它们分别是:map和:map!。映射和缩写的工作方式类似,你给系统一个关键序列,并给系统另外一个关键序列去替换之前的序列。(被替换掉的关键序列通常是VI命令。)

EXINIT环境变量和.exrc文件
有两种方式来个性化VI编辑器。如果你在主目录下生成了一个叫做.exrc的文件,当VI启动的时候,那里面所有的命令都会被读到。另外一种方法是设置一个叫做EXINIT的环境变量。该选项在你的shell的建立文件里被设置。如果你使用/bin/csh (C-Shell),命令如下(被放置在.cshrc文件里面):setenv EXINIT ‘…’。
如果你使用/bin/sh or /bin/ksh,命令如下(被放置在.profile文件中):export EXINIT EXINIT=’…’。
就像例子中所说的,不要放在…。在这个空间中,放置你想要建立的命令。例如,如果你想自动缩进,行编号,以及wrap边界三个字符,setenv命令(对于C shell来说)像这样:setenv EXINIT ‘set ai nuwm=3’。
如果你想在setenv EXINIT中放置不止一个命令,用竖线(|)将命令隔开。例如,在命令模式中,要将“g”命令映射到“G”字符,命令是:map g G,与上面的命令结合,可以得到:setenv EXINIT ‘set ai nuwm=3|map g G’。
如果你想生成叫做.exrc的文件,你可以在文件中放置与EXINIT之后的引用一样的东西。

当终端出现问题的时候恢复你的工作
VI编辑器编辑你的文件的一个临时副本,当编辑结束之后,或者当你叫它保存的时候,它就将临时文件的内容放到原始文件中。如果在你编辑文件的时候发生了故障,VI编辑器会试图保存你正在做的任何工作,并且为了之后的恢复而存储它。(注意:如果在你编辑文件的时候VI死掉了,它会给你发一封邮件告诉你如何恢复它。-r选项代表恢复。如果你正在编辑文件vitalinfo,而你意外退出了,“vi”编辑器的-r选项可以帮你忙。该命令像这样:vi -r vitalinfo。在使用-r选项一次之后,你必须要将你恢复的内容保存到实际的文件中。-r选项只能在每一个失败的VI会话中使用一次。)

有关在工作台上使用VI的警告
当你使用工作台时,必须要知道两件事情:一次(连续)编辑相同的文件许多次,以及改变屏幕的大小。
因为VI编辑你的原始文件的一个副本,并且将那个副本的内容保存到原始文件中,如果你登录了好几次,并且使用VI编辑相同的文件好几次,如果你一次保存在一个窗口上,然后又保存到另外一个窗口上,第一次保存的对于文件的改变会被覆盖掉。确保对于每个文件,你只是运行一个副本。
如果你使用一个工作台的一个终端程序,你可以通过拖动窗口的边界来改变屏幕的大小。如果你对大小还不尽满意,输入以下命令:eval `resize`。如果这个命令无效,另外一个命令如下:eval `/usr/bin/X11/resize`。
如果大小是错误的,编辑器不会正常运行。如果你对于屏幕尺寸存在任何疑问,可以向计算机实验室的管理员求助,他会帮你设置正确的尺寸。

VI命令的总结
以下是按照功能分类的VI命令的一个总结列表。有可能还会有其它命令,可以查看VI的在线手册。为了方便,你可以以文本文件方式保存该文件,然后删除你认为自己不会用的一些命令,并打印出剩下的较短的文件。

剪切与粘贴/删除文字

  • “:指定一个任何命令使用的缓存。在”之后输入一个字母或数字,它们会对应一个缓存。
  • D:从当前光标所在位置向后删除直到行尾。
  • P:在当前光标位置或行之前粘贴专门的缓存。如果没有指定缓存(使用”命名),“P”就使用通用缓存。
  • X:删除光标之前的字符。
  • Y:将当前行整形到指定的缓存。如果没有指定缓存,就使用通用缓存。
  • d:删除,直到where。“dd”删除当前行。一个数字就表示删除那么多行。被删除的内容放置在由”命令指定的缓存中。如果没有指定缓存,就使用通用缓存。
  • p:在当前光标位置或行之后粘贴专门的缓存。如果没有指定缓存(使用”命名),“p”就使用通用缓存。
  • x:删除光标之下的字符。输入一个数字表示要删除多少字符。被删除的字符位于光标之后。
  • y:整形,将结果放到一个缓存中。“yy”整形当前行。输入一个数字表示要整形的行数。可以用”命令来指定缓存。如果没有指定缓存,就使用通用缓存。

插入新的文字

  • A:在当前行之后追加。
  • I:在一行的开头处插入。
  • O:在当前光标位置的上面一个新行进入插入模式。
  • a:进入插入模式,输入的字符会在当前光标位置之后插入。如果在命令之前输入一个数字,那么会插入内容多次。
  • i:进入插入模式,输入的字符会在当前光标位置之前插入。如果在命令之前输入一个数字,那么会插入内容多次。
  • o:在当前光标位置之下的一个新行进入插入模式。

在文件内移动光标

  • ^B:向后回滚一页。输入数字就会回滚那么多页。
  • ^D:向前滚动半个窗口。输入数字会滚动那么多行。
  • ^F:向前滚动一页。输入数字会滚动那么多页。
  • ^H:将光标向左移动一个空格。输入数字会移动那么多个空格。
  • ^J:在同一列向下移动光标一行。输入数字会向下移动那么多行。
  • ^M:移动到下一行的第一个字符处。
  • ^N:在同一列向下移动光标一行。输入数字会向下移动那么多行。
  • ^P:在同一列向上移动光标一行。输入数字会向上移动那么多行。
  • ^U:向后回滚半个窗口。输入数字会回滚那么多行。
  • $:将光标移动到当前行的末尾。输入数字会移动到下面行的末尾。
  • %:将光标移动到匹配的括号处。
  • ^:将光标移动到第一个非空白的字符处。
  • (:将光标移动到一个句子的开头。
  • ):将光标移动到下一个句子的开头。
  • {:将光标移动到前一个段落。
  • }:将光标移动到下一个段落。
  • |:将光标移动到指定的列(由count指定)。
  • +:将光标移动到下一行的第一个非空白字符处。
  • -:将光标移动到之前一行的第一个非空白字符处。
  • _:将光标移动到当前行的第一个非空白字符处。
  • 0:将光标移动到当前行的第一列。
  • B:将光标回移一个字,跳过punctuation。
  • E:将光标向前移动到一个字的结尾,跳过punctuation。
  • G:跳到由count指定的行处。如果没有指定数目,就跳转到文件的末尾。
  • H:将光标移动到屏幕顶端的第一个非空白字符处。
  • L:将光标移动到屏幕底端的第一个非空白字符处。
  • M:将光标移动到屏幕中间的第一个非空白字符处。
  • W:将光标向前移动到一个字的开头,跳过punctuation。
  • b:将光标回移一个字。如果光标在字的中间,就将光标移动到那个字的第一个字符处。
  • e:将光标前移一个字。如果光标在字的中间,就将光标移动到那个字的最后一个字符处。
  • h:将光标向左移动一个字符的位置。
  • j:将光标向下移动一行。
  • k:将光标向上移动一行。
  • l:将光标向右移动一个字符的位置。
  • w:将光标向前移动一个字。如果光标在字的中间,就将光标移动到下一个字的第一个字符处。

在屏幕上移动光标

  • ^E:向前滚动一行。用count指定滚动的行数。
  • ^Y:向后滚动一行。用count指定滚动的行数。
  • z:用以下选项重画屏幕。“z<回车>”将当前行放到屏幕的顶部;“z.”将当前行放到屏幕的中间;“z-”将当前行放到屏幕的底部。如果你在“z”命令之前指定一个数字,它就将当前行变到指定的行处。例如,“16z.”将第16行放到屏幕的中间。

替换文字

  • C:从当前光标位置处变到行的结尾。
  • R:用输入的一系列字符(以Esc键结尾)替换屏幕上的字符。S:改变一整行。
  • c:改变直到。“cc”改变当前行。用count指定改变的行数。
  • r:替换光标下的一个字符。用count指定替换的字符数。
  • s:替换(Substitute)光标下的一个字符,并进入插入模式。用count指定替换的字符数。在最后一个替换的字符处放一个美元($)符号。

搜索文字或字符

  • ,:在相反方向上重复上一个f,F,t或T命令。
  • /:在文件里向下搜索/之后的字符串。
  • ;:重复上一个f,F,t或T命令。
  • :在文件里向上搜索之后的字符串。
  • F:在当前行向后搜索“F”命令指定的字符。如果找到了,就将光标移动到那个位置。
  • N:重复由“/”或“”给出的搜索,不往相反方向搜索。
  • T:在当前行向后搜索“F”命令指定的字符。如果找到了,就移动到那一列。
  • f:在当前行搜索“f”命令之后指定的字符。如果找到了,就将光标移动到那个位置。
  • n:重复上一个“/”或“”搜索。
  • t:在当前行搜索“t”命令之后指定的字符。如果找到了,就将光标移动到那个字符位置之前的一列。

操纵字符/行格式

  • ~:转换光标之下的字符事例(Switch the case of thecharacter under the cursor)。
  • <:Shift the lines up towhere to the left by one shiftwidth. “<<” shifts the currentline to the left,and can be specified with a count。
  • >:Shift the lines up towhere to the right by one shiftwidth. “>>” shifts the currentline to theright, and can be specified with a count。
  • J:将当前行和下一行合并起来。用count指定合并的行数。

保存与退出

  • ^\:退出“VI”模式,进入“EX”模式。EX编辑器是行编辑器,VI就是建立在其上的。重新进入VI的EX命令是“:vi”。
  • Q:退出“VI”模式,进入“EX”模式。ex编辑器是一个逐行(line-by-line)编辑器。重新进入VI的EX命令是“:vi”。
  • ZZ:退出编辑器,如果有任何改动就保存。

其它一些指令

  • ^G:显示当前的文件名和状态。
  • ^L:清除并重画屏幕。
  • ^R:重画屏幕并移除假的行。
  • ^[:退出键。取消部分形成的命令。
  • ^^:回到上次编辑的文件处。
  • !:执行一个shell。如果指定了a,使用!执行的程序将特定的行作为标准输入,同时也会替换带执行程序的标准输出的那些行。“!!”将当前行作为输入来执行一个程序。例如,“!4jsort”会从当前光标位置拿掉五行并执行sort。在键入命令之后,会有一个你可以输入命令的单独的exclamation点。
  • &:重复之前的“:s”命令。
  • .:重复最后一次修改文件的那个命令。
  • ::开始输入一个EX编辑器命令。当用户输入回车的时候,此命令马上执行。
  • @:输入在特定缓存中存储的命令。
  • U:将当前行恢复到光标进入行之前的状态。
  • m:用“m”命令之后的特定字符来标记当前位置。
  • u:撤销对文件所作的最后一次修改。再次输入“u”会恢复修改。

EX命令
VI编辑器建立在另外一个叫做EX的编辑器之上。EX编辑器只通过行来编辑。在VI编辑器中,用:命令来开始键入一个EX命令。以下的列表并不完全,但是给出的命令是用得比较多的。如果用某些命令(如“:s”和“:w”)来修改不止一行,在命令之前必须指定范围。例如,要替换掉从第3行到第15行的内容,命令是“:3,15s/from/this/g”。
:abstring strings
缩写。如果在VI中输入一个与strings相关的字,编辑器会自动插入相应的字。例如,缩写“:ab usa United States ofAmerica”会在输入“usa”的时候插入字“United States of America”。
:mapkeys new-seq
映射。此命令将一个关键字或一个关键字序列映射到另外一个关键字或一个关键字序列。
:q
退出VI。如果对内容有任何改动,编辑器会发出一个警告信息。
:q!
不保存而退出VI。
:s/pattern/to_pattern/options
替换。此命令用to_pattern中的字符串替换指定的pattern。如果没有参数(选项),此命令只是替换第一个出现的pattern。如果给定了“g”,所有出现的pattern都会被替换掉。例如,命令“:1,$s/Dwayne/Dwight/g”会替换掉将所有出现的“Dwayne”替换为“Dwight”。
:set[all]
给VI和EX设定一些个性化的选项。“:set [all]”命令给出了所有可能的选项。
:unastring
移除之前由“:ab”定义的缩写。
:unmkeys
移除由“:map”定义的移除映射。
:vifilename
开始编辑一个新文件。如果没有保存对内容作出的改动,编辑器会给出一个警告。
:w
写出当前文件。
:wfilename
将缓存写到指定的文件名。
:w>> filename
将缓存的内容追加到文件中。
:wq
写缓存并退出。

近20个绚丽实用的jQuery/CSS3侧边栏菜单

Standard

原文:http://www.codeceo.com/article/20-jquery-css3-side-menu.html

jQuery作为一款主流的JavaScript前端开发框架,深受广告开发者的亲睐,同时jQuery有着不计其数的插件,特别是菜单插件更为丰富,本文将要为大家介绍20个绚丽而实用的jQuery侧边栏菜单,这些侧边栏菜单可以用在不同风格的网页上,如果你觉得不错,也可以进入其下载页面下载菜单源代码。

1、jQuery 3D 垂直多级菜单 可筛选菜单项

这是一款手风琴样式的垂直多级菜单,其特点是利用CSS3特性让菜单外观显得3D立体的效果,同时你也可以在搜索框中输入关键字来筛选菜单项。

jquery-3d-menu-with-search

在线演示        源码下载

2、CSS3手机端侧滑菜单 4种滑动菜单特效

这是一款基于CSS3的手机端侧滑菜单,一共有4种侧滑动画特效。这款CSS3菜单的特点是鼠标划过时即可以各种动画方式展开和隐藏菜单项,该动画方式由CSS3中的transition-delay属性来完成,具体效果大家可以看演示。

css3-mobile-slider-menu

在线演示1        在线演示2        在线演示3        在线演示4        源码下载

3、纯CSS3垂直菜单 菜单项滑动动画

这款CSS3菜单的特点是菜单项有一个非常特别的背景,并且背景可随着鼠标滑过而滑动,挺有创意的滑动动画。这款CSS3菜单是垂直样式的,很适合做网页的侧边栏菜单。

pure-css3-ver-slider-menu

在线演示        源码下载

4、jQuery左侧带小图标的多级下拉菜单

今天我们要来分享一款很不错的jQuery左侧带小图标的多级下拉菜单,菜单是垂直的,每一个菜单项带有一个小图标,看起来非常专业。并且菜单支持无限极下拉,所以对各位Web开发者来说非常实用。菜单时基于jQuery的,所以基本可以支持所有的浏览器。

jquery-side-icon-dropdown-menu

在线演示        源码下载

5、CSS3带小图标垂直下拉菜单

这是一款效果非常不错的CSS3垂直下拉菜单,菜单左侧是每一个菜单项的功能小图标,右侧也可以定义一些数字小图标,并且在菜单项最右侧是tooltip的样式。另外,当鼠标滑过菜单项时将会改变菜单的背景颜色,其中的圆角效果由简单的CSS3属性完成。

css3-dropdown-menu-icon

在线演示        源码下载

6、jQuery多层级垂直手风琴菜单

这款手风琴菜单是多层级的,你可以通过HTML结构生成任意层级的菜单。由于是基于jQuery的,因此这款手风琴菜单的兼容性还不错。

jquery-level-accord-menu

在线演示        源码下载

7、CSS3垂直图标菜单 带Tooltip提示框

今天我们要来分享一款CSS3菜单,菜单外观很漂亮,是垂直排列的小图标,鼠标滑过菜单项时,菜单项的背景会填充渐变的颜色,另外还会弹出该菜单项描述的Tooltip提示框。之前我们也分享过很多CSS3垂直菜单,像这款CSS3二级下拉动画菜单 菜单背景滑动动画纯CSS3垂直动画菜单等都是非常不错的CSS3垂直菜单按钮。

css3-ver-nav-with-icon

在线演示        源码下载

8、CSS3手风琴下拉菜单 支持多菜单展开

这又是款基于CSS3的下拉菜单,这款CSS3菜单是手风琴样式的,今天的这款CSS3手风琴菜单也类似,菜单具有3种模式,一种是同时展开多个菜单,一种是只能同时展开一个菜单,还有一种是可以默认展开一个菜单。应该说,这款CSS3手风琴菜单非常的实用。

css3-accord-menu-mult-flip

在线演示        源码下载

9、HTML5/CSS3仿Google Play垂直菜单

这款CSS3菜单也是垂直菜单,是一款仿Google Play的垂直菜单,另外菜单左侧还有非常漂亮的小图标。

css3-google-play-store-menu

在线演示        源码下载

10、CSS3手风琴菜单 可同时折叠多个菜单

这次分享的CSS3手风琴菜单可以同时折叠展开多个菜单项,菜单整体是黑色的风格,并且在每一个菜单项上都有很漂亮的小图标,小图标像是嵌入在里面一样很有质感。

css3-multiple-accordion-menu

在线演示        源码下载

11、CSS3垂直手风琴折叠菜单

这款CSS3垂直手风琴折叠菜单也非常不错,这款CSS3手风琴菜单的每一个菜单项都有小图标,而且只能有一项展开,更有意思的是,在菜单折叠和展开式右侧的箭头也会有不错的动画效果。

css3-ver-accordion-menu

在线演示        源码下载

12、纯CSS3垂直动画菜单 效果很酷

今天我们来分享一款CSS3垂直菜单,这款垂直菜单不仅有漂亮的小图标,而且鼠标滑过时还有非常酷的CSS3动画,大家可以试试这款CSS3垂直菜单。

pure-css3-vertical-menu

在线演示        源码下载

13、CSS3二级下拉动画菜单 菜单背景滑动动画

这款CSS3菜单的特点是在菜单展开时,菜单的背景会出现滑动的动画效果。

css3-animation-bg-menu

在线演示        源码下载

14、jQuery/CSS3带未读提醒的垂直菜单

这是一款基于jQuery和CSS3的垂直动画菜单,这款jQuery菜单的特点是菜单整体悬浮在一张大气的背景图片上,很有立体的视觉效果。其次这款菜单带有信箱未读提醒,并且不断地闪烁来提示用户打开邮件箱check邮件。

jquery-css3-menu-with-inbox

在线演示        源码下载

15、jQuery/CSS3可折叠侧边栏菜单

这次我们要分享的一款很棒的jQuery菜单插件,这款菜单插件是可折叠的侧边栏菜单,菜单的特点是点击按钮可以展开和折叠菜单,并伴随动画效果。而且每一个菜单项都有一个小图标,非常清新漂亮。当然折叠的效果需要CSS3的支持。

jquery-css3-slide-menu

在线演示        源码下载

16、jQuery仿Google Nexus菜单样式插件

这是一款基于jQuery和CSS3的多功能菜单,菜单样式是仿Google Nexus的,菜单整体看上去是一个封闭的结构,也就是说既有水平菜单,也有垂直菜单,而且每一个菜单项的左侧都有一个漂亮的小图标,是一款外观非常不错的jQuery菜单导航。

jquery-google-nexus

在线演示        源码下载

17、清新小图标的HTML5/CSS3侧边栏菜单

这款CSS3侧边栏菜单和之前这款菜单类似,也带有漂亮可爱的小图标,鼠标滑过菜单项时背景会出现淡入淡出的效果。

html5-css-side-menu-with-icon

在线演示        源码下载

18、CSS3垂直菜单 菜单有立体动画视觉

这款基于CSS3的垂直菜单实现很简单,该CSS3垂直菜单有几个特点:

  1. 菜单外观呈立体视觉效果,非常有质感
  2. 鼠标滑过菜单项时,菜单项会出现伸缩动画。

css3-vertical-menu

在线演示        源码下载

19、CSS3手风琴菜单 下拉展开带弹性动画

这款是CSS3手风琴菜单,菜单项在展开和收缩的时候菜单项会有弹性动画效果。每一层父级菜单有一个小三角,菜单项在展开的时候这个小三角也会出现动画,非常酷。

css3-accordion-menu

在线演示        源码下载

以上这些jQuery侧边栏菜单是不是对你有所帮助,欢迎你的评价。

10大好用的Linux实用工具推荐

Standard

https://winclient.cn/10-useful-utilities-linux/

本文我们收集了对 Linux 用户非常有用的 10 个工具,其中包括网络监控、系统审计或其它有用命令,这 10 个 Linux工具可以帮助大家提高工作和使用效率,非常实用。

1. w

对,你没看错,就是 w 命令。使用该命令我们可以查看到当前登录系统的用户是谁,以及执行了哪些命令。

10大好用的Linux实用工具推荐

2. nmon

Nmon 是一个可以监控当前系统性能的小工具,使用之前需要先用如下命令进行安装:

  1. sudo aptget install nmon

安装好后执行 nmon 命令即可打开:

  1. nmon

10大好用的Linux实用工具推荐

nmon 可以查看网络、CPU、内存和磁盘的使用情况。

打开之后按 c 查看 CPU 信息:

10大好用的Linux实用工具推荐

打开之后按 n 查看网络信息:

10大好用的Linux实用工具推荐

3. ncdu

ncdu 命令可以用来查看和分析 Linux 中各目录对磁盘空间占用情况的工具,请使用如下命令进行安装:

  1. aptget install ncdu

安装好后执行如下命令即可从根目录开始分析:

  1. ncdu /

10大好用的Linux实用工具推荐

注意:执行上述命令会占用大量磁盘 I/O

分析完成后,会生成类似如下截图的输出:

10大好用的Linux实用工具推荐

我们可以在结果界面按 n 按名称进行排序或按 s 按大小进行排序。

4. slurm

slurm 是一个网卡带宽监控命令行实用程序,它会自动生成 ASCII 图形输出。使用之前先用如下命令进行安装:

  1. aptget install slurm

使用如下命令进行输出:

  1. slurm i <网卡名称>

10大好用的Linux实用工具推荐

slurm 界面中可以执行如下选项:

  • I:显示lx/tx状态
  • c:切换到经典界面
  • r:手动刷新界面
  • q:退出工具

5. findmnt

Findmnt 是一个 Linux 内置的命令行工具,它主要用于查找挂载的文件系统状态。Findmnt 可以查看到当前系统中已挂载的设备,在必要时还可进行 mount 或 unmount 操作。

执行 findmnt 命令后会看到如下输入:

10大好用的Linux实用工具推荐

当然,还有如下参数可用:

  • findmnt -l :以列表方式进行输出
  • findmnt -s :输出 fstab 中挂载的设备
  • findmnt -t ext4 :按文件系统类型进行输出

6. dstat

dstat 是一个可以非常灵活使用和进行组合使用的工具,它可用于监控内存、进行、网络及磁盘性能,可用于替代  ifstat、iostat、dmstat等工具。使用之前需先执行如下命令进行安装:

  1. aptget install dstat

执行如下命令可以看到所有监控数据:

  1. dstat

10大好用的Linux实用工具推荐

其可选参数非常多,常用的有:

  • dstat -c : 监控CPU
  • dstat -cdl -D sda1 :监控CPU详细信息
  • dstat -d :监控磁盘

7. saidar

saidar 是另一个 CLI 系统数据监控和统计工具,可提供有关磁盘、网络、存储和 SWAP 的监控信息。使用之前需先使用如下命令进行安装:

  1. sudo aptget install saidar

安装完成后可直接执行 saidar 进行输出,但我们通常使用带参数的命令生成带颜色输出:

  1. saidar c

10大好用的Linux实用工具推荐

8. ss

ss 全称 socket statistics,是一个可以替代 netstat 的网络连接查看工具。

直接执行 ss 即可进行查看:

10大好用的Linux实用工具推荐

常用参数有:

ss -A tcp :指定查看协议

ss -ltp :显示进程名称和 PID

9. ccze

ccze 非常有用,它可以用不同颜色高亮日志,协助管理员进行区分和查看分析。使用之前需先使用如下命令进行安装:

  1. aptget install ccze

我们可以使用类似如下方式进行使用:

  1. tailf /var/log/syslog | ccze

10大好用的Linux实用工具推荐

而使用 ccze -l 参数可以查看其支持的日志类型。

10大好用的Linux实用工具推荐

10. ranwhen.py

我们最后介绍的 ranwhen.py 是一个 python 工具,它可以以图形方式显示系统活动。

要使用该工具需要先安装 python 语言支持:

  1. sudo aptaddrepository ppa:fkrull/deadsnakes
  2. sudo aptget update
  3. sudo aptget install python3.2

然后下载ranwhen.py

  1. wget https://github.com/p-e-w/ranwhen/archive/master.zip
  2. unzip master.zip && cd ranwhenmaster

使用如下命令即可执行ranwhen.py

  1. python3.2 ranwhen.py

10大好用的Linux实用工具推荐

小结

本文介绍的 10 大好用 Linux 实用工具都还比较有意思,希望大家能喜欢。

c#中的Struct与Byte[]互转

Standard

原文:http://www.cnblogs.com/juneapple/articles/1769016.html

c#中要让struct转换为byte[],首先要在struct申明中说明struct的结构,如下:


/// <summary>
/// 数据包内容的头信息
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TransHead
{
/// <summary>
/// 本结构的字节大小
/// </summary>
public static readonly int Size = Marshal.SizeOf(typeof(TransHead));

/// <summary>
/// 请求类型
/// </summary>
[MarshalAs(UnmanagedType.I4)]
public RequestType Type;
/// <summary>
/// 请求子类型
/// </summary>
[MarshalAs(UnmanagedType.I4)]
public REFRESH SubType;
/// <summary>
/// 协议版本
/// </summary>
public int Edition;
/// <summary>
/// 标识(系统返回值)
/// </summary>
public int Flag;
}
/// <summary>
/// 数据包内容,头信息加定长数据
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TransHeadAndData
{
/// <summary>
/// 本结构的字节大小
/// </summary>
public static readonly int Size = Marshal.SizeOf(typeof(TransHeadAndData));
/// <summary>
/// 本结构数据体的字节大小(结构大小-结构头)
/// </summary>
public static readonly int SubSize = Size - TransHead.Size;

[MarshalAs(UnmanagedType.Struct)]
public TransHead DataHead;

/// <summary>
/// 具体数据 x460
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 460)]
public byte[] Data;
}
public struct TransUplink
{
/// <summary>
/// 本结构的字节大小
/// </summary>
public static readonly int Size = Marshal.SizeOf(typeof(TransUplink));// - 4;
/// <summary>
/// 数据类型
/// </summary>
[MarshalAs(UnmanagedType.I4)]
public RequestType Type;
/// <summary>
/// /// 数据内容
/// </summary>
[MarshalAs(UnmanagedType.Struct)]
public TransHeadAndData Data;
///// <summary>
///// 发送上去的数据头长度(仅用户客户端,用于接收数据后删除重发列表时的计算,计算结构大小时不加入该项)
///// </summary>
//public int HeadLenght;
}

以下是基本转换方法


/// <summary>
/// 将struct类型转换为byte[]
/// </summary>
public static byte[] StructToBytes(object structObj, int size)
{
IntPtr buffer = Marshal.AllocHGlobal(size);
try//struct_bytes转换
{
Marshal.StructureToPtr(structObj, buffer, false);
byte[] bytes = new byte[size];
Marshal.Copy(buffer, bytes, 0, size);
return bytes;
}
catch (Exception ex)
{
throw new Exception("Error in StructToBytes ! " + ex.Message);
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
/// <summary>
/// 将byte[]还原为指定的struct,该函数的泛型仅用于自定义结构
/// startIndex:数组中 Copy 开始位置的从零开始的索引。
/// length:要复制的数组元素的数目。
/// </summary>
public static T BytesToStruct<T>(byte[] bytes, int startIndex, int length)
{
if (bytes == null) return default(T);
if (bytes.Length <= 0) return default(T);
IntPtr buffer = Marshal.AllocHGlobal(length);
try//struct_bytes转换
{
Marshal.Copy(bytes, startIndex, buffer, length);
return (T)Marshal.PtrToStructure(buffer, typeof(T));
}
catch(Exception ex)
{
throw new Exception("Error in BytesToStruct ! " + ex.Message);
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}

犹豫频繁的重复调用 IntPtr buffer = Marshal.AllocHGlobal(size);语句,会出现缓存创建错误的情况(溢出之类的错误)

所以在写入列表型数据时使用下面的优化方法


/// <summary>
/// 将List<struct>类型转换为byte[]并写入流
/// </summary>
public static void SetFromListToStream<T>(List<T> inList, int size, FS.FileStreamEx stream, long offset, int listStart, int listCount)
{
IntPtr buffer = Marshal.AllocHGlobal(size);
try//struct_bytes转换
{
if (listCount < 0) listCount = inList.Count;
//若需写入的数据条数太大,则将其分为N块批量写入
int num = listCount;
if (num > 100)
{
num = num / 100;
}
else
{
num = 1;
}

byte[] numByte = new byte[num * size];
byte[] bytes = new byte[size];
for (int i = 0; i < listCount; i++)
{
//批量写入模式
if (num > 1 && num + i < listCount)
{

for (int j = 0; j < num; j++)
{
Marshal.StructureToPtr(inList[listStart + i + j], buffer, false);
Marshal.Copy(buffer, numByte, j * size, size);
}
stream.WriteBytes(offset + i * size, numByte);
i += num - 1;//-1因为外面的for循环中i是+1的
}
//逐条写入模式
else
{
Marshal.StructureToPtr(inList[listStart + i], buffer, false);
Marshal.Copy(buffer, bytes, 0, size);
stream.WriteBytes(offset + i * size, bytes);
}
}
}
catch(Exception ex)
{
throw new Exception("Error in SetFromListToStream ! " + ex.Message);
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}

同理,在读出时也应该使用优化方法


public static int SetFromStreamToList<T>(ref List<T> inList, int size, FS.FileStreamEx stream, long offset, int lenght, bool clear)
{
if (clear) inList.Clear();
if (offset < 0) offset = 0;
if (lenght <= 0) lenght = (int)(stream.Length - offset);
byte[] tmpByte = stream.ReadBytes(offset, lenght);
if (tmpByte == null) return ErrCode.Success;
return SetFromBytesToList<T>(ref inList, size, tmpByte, 0, 0);
}

/// <summary>
/// 将byte[]内容转为指定结构,并添加到相应的List中,该函数的泛型仅用于自定义结构
/// </summary>
/// <typeparam name="T">将要转换的类型,同时也是List的类型</typeparam>
/// <param name="inList">将要添加数据的List</param>
/// <param name="size">指定类型的长度,传入长度是为了避免每次都去计算结构长度</param>
/// <param name="inByte">将要转换的数据</param>
/// <param name="offset">便宜量,从数组中指定位置开始转换,取值范围:0至lenght</param>
/// <param name="lenght">将要转换的数据长度,
/// 若lenght大于等于inByte.Lenght则lenght=inByte.Lenght;
/// 若lenght小于等于0则lenght=inByte.Lenght</param>
/// <param name="clear">添加前是否清除旧数据</param>
/// <returns>是否成功</returns>
public static int SetFromBytesToList<T>(ref List<T> inList, int size, byte[] inByte, long offset, int lenght, bool clear)
{
lock (inList)
{
if (clear) inList.Clear();
if (lenght <= 0) lenght = inByte.Length;
if (lenght <= 0) return ErrCode.Success;

IntPtr buffer = Marshal.AllocHGlobal(size);
int i;
try//struct_bytes转换
{
for (i = (int)offset; i + size <= lenght; i += size)
{
Marshal.Copy(inByte, i, buffer, size);
inList.Add((T)Marshal.PtrToStructure(buffer, typeof(T)));
}
}
catch(Exception ex)
{
throw new Exception("Error in SetFromBytesToList ! " + ex.Message);
}
finally
{
Marshal.FreeHGlobal(buffer);
}
return ErrCode.Success;
}
}