vim g s 替换区别

 
 
PS:一篇好文收藏备用,今天用它解决了一个大问题。

发信人: vale (浅谷), 信区: VIM
标  题: global命令详解 
发信站: 水木社区 (Fri Jun 15 17:05:55 2007), 站内

global命令是Vim最强大的命令之一(个人认为是No.1),将其摸透用熟可以事半功倍,
本文总结了版上的一些经典问题,结合自己的使用和理解,试图通过实例详细介绍一下
其用法。示例难度不一,有些并没有多少实用性,为题而生,读者各取所需吧。示例说
明并不非常细致,以免罗唆。每节标题下列出了所涉及的内容在Vim help中的位置,以
供查找。文中用词未必标准(我没看过Vim中文帮助),观点也难免有错,请大家指正!

1. global命令形式

:h :g

:h 12.4

:[range]global/{pattern}/{command}

global命令在[range]指定的文本范围内(缺省为整个文件)查找{pattern},然后对匹
配到的行执行命令{command},如果希望对没匹配上的行执行命令,则使用global!或vg
lobal命令。

先来看Vim用户手册里的一个经典例子。

【例1】倒序文件行(即unix下的tac命令)

:g/^/m 0

这条命令用行首标记/^/匹配文件的所有行(这是查找的一个常用技巧,如果用/./则是
匹配非空行,不满足本例要求),然后用move命令依次将每行移到第一行(第0行的下一
行),从而实现了倒序功能。

global命令实际上是分成两步执行:首先扫描[range]指定范围内的所有行,给匹配{pa
ttern}的行打上标记;然后依次对打有标记的行执行{command}命令,如果被标记的行在
对之前匹配行的命令操作中被删除、移动或合并,则其标记自动消失,而不对该行执行
{command}命令。标记的概念很重要,以例说明。

【例2】删除偶数行

:g/^/+1 d

这条命令也是匹配所有行,然后隔行删除(其中+1用以定位于当前行的下一行)。为什
么是隔行呢?因为在对第一行执行+1 d命令时删除的是第二行,而第二行虽然也被标记
了,但已不存在了,因此不会执行删除第三行的命令。

本例也可以用normal命令实现:

:%norm jdd

%指定整个文件,然后依次执行普通模式下的jdd,即下移删除一行。与global命令不同
之处在于,%norm是按照行号顺序执行,在第一行时删除了第二行,后面的所有行号都减
一,因此在第二行执行jdd时删除的是原来的第四行。也就是说,global命令是通过偶数
行标记的消失实现的,而normal命令是通过后续行的自动前移实现的。

【例3】删除奇数行

:g/^/d|m.

光是:g/^/d显然不行,这会删除所有行,我们需要用move命令把偶数行的标记去掉。当
然,本例可以很简单的转换成【例2】,在此只是用来强调标记的概念。

本例若想用normal命令实现比较有意思,%norm dd也同样会删除整个文件,%norm jkdd
就可以,我不知道两者为什么不同,可能和normal命令内部的运行机制有关。

2. global与substitute

:h 10.4

:helpg ms-word\c

不少vimmer觉得这两个命令差不多,的确,它们的形式很相似,都是要进行查找匹配,
只不过substitute执行的是替换而global执行的其它命令(当然,substitute缺省的[r
ange]是当前行,这点也不同)。先看两个例子,体会一下:s和:g不同的思维方式。

【例4】double所有行

:%s/.*/&\r&/   #其中&不懂 懂得解释下呗  个人感觉 &代表的就是匹配的字符串

例如:
  g/^[[:alpha:]]/ s/\w*/\u&/g    #其中\w带包[a-zA-Z_0-9] \u代表大写字母 把单词更改为首字母大写

\| 表示"或"的意思,也就是说,只要满足两边任意一个匹配都行,需要注意的是,在括号里面,是把左右两个部
分作为整体,而不是只有一个字符,所以不用再加括号了

:g/^[[:alpha:]]/s/\<\w*\>\|[[:alpha:]]*\'[[:alpha:]]*/\u&/g

编辑前:
############# src ##############
<li>
don't repeat yourself 4 </li> <li>
make it easy to reuse
</li>~ 编辑后:
############# src ##############
<li>
Don't Repeat Yourself 4 </li> <li>
Make It Easy To Reuse
</li>~
~
命令:g/^[[:alpha:]]/s/\<\w*\>\|[[:alpha:]]*\'[[:alpha:]]*/\u&/g
也可以 g/^[[:alpha:]]/s/\<\w*\>/\u&/g

:g/^/t.

substitue是查找任意行,然后替换为两行夹回车;global是将每一行复制(:t就是:co
py)到自己下面,更加清晰明了。

【例5】把以回车排版、以空行分段的文本变成以回车分段的文本

很多txt格式的ebook,以及像vim help这样的文本,每行的字符数受限,段之间用空行
分隔。若把它们拷贝到word里,那些硬回车和空行就比较讨厌了,虽然word里也有自动
调整格式的功能,不过在Vim里搞定更是小菜一碟。先看看用替换如何实现。

:s/\n\n\@!/ /

\n\n\@!是查找后面不跟回车的回车(关于\@!的用法请:h /\@!,在此不多说了),然后
替换为空格,也就是去掉用于排版的回车。global命令则完全是另一种思路。

:g/./,/^$/j

/./标记非空行,/^$/查找其后的空行,然后对二者之间的行进行合并操作。也许有人会
问,段中的每一行会不会都执行了j命令?前面已经说过,在之前操作中消失掉的标记行
不执行操作命令,在处理每段第一行时已经把段内的其余行都合并了,所以每段只会执
行一次j命令。这条命令使用global标记做为[range]的起始行,这样的用法后面还会详
述。

global经常与substitute组合使用,用前者定位满足一定条件的行,用后者在这些行中
进行查找替换。如:

【例6】将aaa替换成bbb,除非该行中有ccc或者ddd

:v/ccc\|ddd/s/aaa/bbb/g

【例7】将aaa替换成bbb,条件是该行中有ccc但不能有ddd

如何写出一个匹配aaa并满足行内有ccc但不能有ddd的正则表达式?我不知道。即便能写
出来,也必定极其复杂。用global命令则并不困难:

:g/ccc/if getline('.') !~ 'ddd' | s/aaa/bbb/g

该命令首先标记匹配ccc的行,然后执行if命令(if也是ex命令!),getline函数取得
当前行,然后判断是否匹配ddd,如果不匹配(!~的求值为true)则执行替换。要掌握这
样的用法需要对ex命令、Vim函数和表达式有一定了解才行,实际上,这条命令已经是一
个快捷版的脚本了。可能有人会想,把g和v连起来用不就行了么,可惜global命令不支
持(恐怕也没法支持)嵌套。

3. global标志的[range]用法

:h range

在global命令第一步中所设的标记,可以被用来为{command}命令设定各种形式的[rang
e]。在【例2】和【例5】中都已使用了这一技巧,灵活使用[range],是一项重要的基本
功。先看看【例2】和【例3】的一般化问题。

【例8】每n行中,删除前/后m行(例如,每10行删除前/后3行)

:g/^/,+2 d | ,+6 m -1

:g/^/,+6 m -1 | +1,+3 d

这两个命令还是利用move来清除保留行的标志,需要注意的是执行第二个命令时的当前
行是第一个命令寻址并执行后的位置。再看两个更实用点的例子。

【例9】提取条件编译内容。例如,在一个多平台的C程序里有大量的条件编译代码:

#ifdef WIN32

XXX1

XXX2

#endif

...

#ifdef WIN32

XXX3

XXX4

#else

YYY1

YYY2

#endif

现在用global命令把Win32平台下代码提取出来,拷贝到文件末:

:g/#ifdef WIN32/+1,/#else\|#endif/-1 t $

t命令的[range]是由逗号分隔,起始行是/#ifdef WIN32/标记行的下一行,结束行是一
个查找定位,是在起始行后面出现的#endif或#else的上一行,t将二者间的内容复制到
末尾。

【例10】提取上述C程序中的非Win32平台的代码(YYY部分)

首先说明一下,这个例子比前例要复杂的多,主要涉及的是[range]的操作,已经和glo
bal命令没多少关系,大可不看。加到这的目的是把问题说完,供喜欢细抠的朋友参考。
本例的复杂性在于:首先,不能简单的用#else和#endif定位,因为代码中可能有其它的
条件编译,我们必须要将查找范围限定在#ifdef WIN32的block中;另外,在block中可
能并没有#else部分,这会给定位带来很大麻烦。解决方法是:

:try | g/#ifdef WIN32//#else/+1, /#endif/-1 t $ | endtry

先不管try和endtry,只看中间的global部分:找到WIN32,再向后找到#else,将其下一
行作为[range]的起始行,然后从当前的光标(WIN32所在行,而非刚找到的#else的下一
行)向下找到#endif,将其上一行作为[range]的结束行,然后执行t命令。但对于没有
#else的block,如第一段代码,[range]的起始行是YYY1,而结束行是XXX2(因为查找#
endif时是从第一行开始的,而不是从YYY1开始),这是一个非法的[range],会引起ex
ception,如果不放在try里面global命令就会立刻停止。

与逗号(,)不同,如果[range]是用分号(;)分隔的,则会使得当前光标移至起始行,在查
找#endif时是从#else的下一行开始,这样就产生非法[range],用不着try,但带来的问
题是:没有#else的block会错误的把后面block中的#else部分找出来。

4. global与Vim脚本

:h script

:h expression

经常有人问:XxEditor有个什么功能,Vim支持么?很可能不支持,因为Vim不大会为特
定用户群提供非一般化的功能,但很少有什么功能不能在Vim定制出来,如果是你常用,
就加到你的vimrc或者plugin里。脚本就是定制Vim的一种利器。本文不讨论脚本的编写
,而是介绍如何实用global实现类似脚本的功能,实际上,就是利用命令提供的机制,
做一个简化的脚本。

【例11】计算文件中数字列之和(或其它运算)

:let i=0

:g/^/let i+=str2nr(getline('.'))

:echo i

首先定义变量i并清零,然后用str2nr函数把当前行转成数字累加到i中,注意Vim不支持
浮点数。global在这里实际上是替代了脚本里的for循环。

Vim中最常见的一个问题是如何产生一列递增数字,有很多解决办法,调用外部命令,录
宏,用substitute命令,还有专门的插件,而用global命令,可以实现一些更高级的功
能。见下例。

【例12】给有效代码行添加标号

在_Data Structures and Algorithm Analysis in C_一书中,作者为了便于讨论,将代
码中的有效行加上注释标号,例如:

/*  1 */  unsigned int factorial( unsigned int n )

{

/*  2 */      if( n <= 1 )

/*  3 */          return 1;

/*  4 */      return( n * factorial(n-1) );

}

为了在添加标号后能对齐,我们预先在每行代码前面插入足够多的空格(这当然很简单
),然后用global命令自动添加标号:

:let i=1 | g/\a/s/ \{8}/\=printf("\/* %2d *\/",i)/ | let i+=1

其中变量i用来记录标号,g命令查找有字母的行,然后把前8个空格替换成注释标号,每
行处理完成后标号加一。替换中用到了/\=,一个非常有用的功能。

5.小结

要用好global命令并非易事,命令中的每一部分都值得仔细研究:只有掌握了range原理
,才能自如的在文件中定位;只有精通pattern,才能有效的匹配到想要找的行;只有熟
悉ex命令,才能选用最合适的功能进行操作;只有对变量、表达式、函数等内容有一定
了解,才更能让global命令实现脚本的功能。总之,global是一个非常好的框架,对Vi
m越是熟悉,就越能将其种种武器架设在其上使用,发挥更大的威力。

global当然并非万能,功能也有所欠缺,最主要的问题是只能用正则表达式来标志匹配
行,如果能用任意表达式来标记(或者从另一个角度,如前mv版主runsnake所说,引入
求值正则表达式),则可实现更加方便功能。比如前述的几个删除特定行的问题,将会
有简单而统一的解决方法。上述例子如果用sed、awk等专门的文本处理工具,或者perl
之类的script语言也非难事,有些实现起来会更加方便。本文提供的Vim解决方法未必简
单,甚至可能是难于理解,目的在于介绍global的使用。对于那些不会或者不能使用其
它工具的朋友,参考价值可能更大一些。其实Vim的功能实在很丰富,值得我们深入学习
。打个不恰当的比方,少林七十二绝技固然高妙,会的越多自然功力越强,不过只要会
上一门六脉神剑或小无相功,也足以独步江湖了。

 
转:http://xu020408.blog.163.com/blog/static/2654892010102211572875/

vim g s 替换区别的更多相关文章

  1. vim g 和 % 区别

    vim中的g(global)和%的区别: g:全局的 s/pattern/replacement/  : 替换行中出现的每一个pattern g/pattern/s/pattern/replaceme ...

  2. vim的全局替换

    本文出自   http://blog.csdn.net/shuangde800 本文是在学习<使用vi编辑器, Lamb & Robbins编著>时在所记的笔记. 本文内容: 基本 ...

  3. 【转载】vim 中如何替换选中行或指定几行内的文本

    https://segmentfault.com/q/1010000002552573/a-1020000002552589 :'<,'>s/替换项/替换为/g 以下命令将文中所有的字符串 ...

  4. Vim正则表达式匹配替换字符串

    /********************************************************************** * Vim正则表达式匹配替换字符串 * 说明: * 用V ...

  5. vim的全局替换[zz]&把字符替换成回车

    本文出自   http://blog.csdn.net/shuangde800   本文是在学习<使用vi编辑器, Lamb & Robbins编著>时在所记的笔记.   本文内容 ...

  6. vim 查找和替换命令 替换/n和\n

    一. 字符串的查找 1. vim 中用 / 和 ? 来查找字符串,两者的区别是: /string 会高亮显示光标后匹配的第一个字符串,回车后光标移到该字符串的第一个字母: ?string 会高亮显示光 ...

  7. Vim查找与替换命令大全,功能完爆IDE!

    Vi/Vim 可以说是文本编辑中的一代传奇人物,直至现在,它仍然在高级程序员的武器库中占有一席之地.每个 Linux 发行版默认都包含Vim ,而且即使你不是 Linux 系统用户,你也可以安装 Vi ...

  8. vim g s 对比

    vim g s http://blog.chinaunix.net/uid-10597892-id-3311441.html

  9. Linux 下Shell 脚本几种基本命令替换区别

    Shell 脚本几种基本命令替换区别 前言:因为工作需要,需要编写 shell script .编写大量 shell script 时,累计了大量经验,也让自己开始迷糊几种函数输出调用的区别.后面和 ...

随机推荐

  1. TopK-微博今日热门话题

    大纲 TopK on single node TopK on multiple nodes Realtime topK with low QPS Realtime topK with high QPS ...

  2. Spread.js 上下级关系

  3. 【bzoj3091】城市旅行 LCT区间合并

    题目描述 输入 输出 样例输入 4 5 1 3 2 5 1 2 1 3 2 4 4 2 4 1 2 4 2 3 4 3 1 4 1 4 1 4 样例输出 16/3 6/1 题解 LCT区间合并 前三个 ...

  4. docker (centOS 7) 使用笔记5 - weave网络

    weave官网 https://www.weave.works 1. 下载安装 sudo curl -L git.io/weave -o /usr/local/bin/weave sudo chmod ...

  5. This dependency was not found: * !!vue-style-loader!css-loader? 解决方案

    但是当你新建一个vue项目时,需要重新安装stylus,否则报错: This dependency was not found: * !!vue-style-loader!css-loader?{&q ...

  6. 【09】Vue 之 Vuex 数据通信

    9.1. 引言 Vue组件化做的确实非常彻底,它独有的vue单文件组件也是做的非常有特色.组件化的同时带来的是:组件之间的数据共享和通信的难题. 尤其Vue组件设计的就是,父组件通过子组件的prop进 ...

  7. BZOJ 2728: [HNOI2012]与非

    2728: [HNOI2012]与非 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 786  Solved: 371[Submit][Status][ ...

  8. input输入框与元素间有间隙

    <div class="container"> <button>1</button> <button>2</button> ...

  9. Java中的IO基本用法

    先贴一下我在作业中用到的三种文件输入辅助类.三种文件输出辅助类 public class BuffIn implements InHelp{ private BufferedReader buffer ...

  10. XmlRootElement

    作用是 java对象 <---> xml文件   之间的转换 JAXB Annotation @XmlRootElement   // xml 文件的根元素 @XmlElement @Xm ...