(转)Vim 脚本语言

2012 年 10 月 20 日 by name5566 Categories: Computer
Science
, Tools

参考文献列表:
http://vimdoc.sourceforge.net/htmldoc/usr_41.html

本文面向一些有编程经验的人(特别是有 C/C++、Java 等编程经验的人),因此对一些显而易见的知识点未做详细的阐述。

Introduction

我们在 Vim 脚本文件中编写 Vim 脚本。常见的 vimrc、Syntax 等文件都是 Vim 脚本文件。我们来看一个简单的例子。首先打开 Vim
创建一个新文件 test.vim 并输入:

  1. :let i = 1
  2. :while i < 5
  3. : echo "count is" i
  4. : let i += 1
  5. :endwhile

保存后敲击命令(这里假定 test.vim 在 Vim 当前工作目录下)

  1. :source test.vim

我们可以看到输出如下:

  1. count is 1
  2. count is 2
  3. count is 3
  4. count is 4

从上面的例子我们可以看到,Vim 脚本实际上是由冒号命令组成的。其实,当我们在脚本文件中编写脚本时,冒号是可以省略不写的。回到上面的例子中:

  1. let 命令用于变量的赋值,通常的形式为:
    1. let {variable} = {expression}

  2. while 命令和 endwhile 命令总是配合在一起使用,通常的形式为:
    1. while {condition}
    2. {statements}
    3. endwhile

  3. echo 命令用于打印其参数,此例子中 echo 命令有两个参数,字符串 "count is" 和变量 i

变量(Variables)

使用 let 命令可以打印出当前定义的变量。首先来了解一下变量的作用域:

  1. 定义一个全局变量 var 其可以在任何地方使用:
    1. let var = 1
    2. # 或者这样写
    3. let g:var = 1

  2. 定义一个局部变量 var 其只能在某个脚本文件中使用:
    1. let s:var = 1

  3. 定义一个变量 var 其只能在某个 buffer 中使用:
    1. let b:var = 1

  4. 定义一个变量 var 其只能在某个 window 中使用:
    1. let w:var = 1

另外,还有一类变量 v:name 其为 Vim 预先定义的变量。如果需要删除一个变量,使用 unlet 命令,例如删除 s:var 变量:

  1. unlet s:var

如果你不确定某个变量是否存在,不希望在使用 unlet 命令的时候出现一个错误消息,那么使用强制命令修饰符:

  1. unlet! s:var

另外需要注意的是(和某些脚本语言不同)变量的生命周期,局部变量不会因为脚本文件执行完成而销毁:

  1. if !exists("s:call_count")
  2. let s:call_count = 0
  3. endif
  4. let s:call_count = s:call_count + 1
  5. echo "called" s:call_count "times"

我们执行这个脚本多次,输入结果为:

  1. called 1 times
  2. called 2 times
  3. ...

这里稍加说明的是 if !exists("s:call_count"),首先 ! 这里的含义是取反,而不是强制命令修饰符(注意 ! 符号的位置),另外
exists 不是一个命令,而是一个函数,函数使用括号包裹住参数列表而命令不需要括号。

在 Vim 中,0 为 false,非 0 为 true,Vim 很多时候会自动转换一个字符串为一个数值以确定其为 true 还是 false:

  1. if "true"

这里,"true" 会被转化为数值 0。

我们还有一些有用的东西:

  1. $NAME 为环境变量 NAME 的值

  2. &name 为选项 name 的值

  3. @r 为寄存器 r 的值

Vim 中字符串可以使用 " 或者 ‘ 字符包裹,只有 " 字符包裹的字符串支持转义字符,例如:

  1. echo ‘\n‘

输出 \n(而非一个空行)。

语句(Statements)

Vim 条件控制(和其他语言类似):

  1. if {condition}
  2. {statements}
  3. elseif {condition}
  4. {statements}
  5. else
  6. {statements}
  7. endif

其中 elseif 和 else 是可选的。Vim 还支持条件控制表达式 a ? b : c(同于 C/C++ 等语言)

Vim 逻辑和算术操作符:

  1. a == b 等于

  2. a != b 不等于

  3. a > b 大于

  4. a >= b 大于等于

  5. a < b 小于

  6. a <= 小于等于

  7. a + b 加

  8. a - b 减

  9. a * b 乘

  10. a / b 除

  11. a % b 模

对于字符串还有两个运算符用于进行字符串匹配:

  1. a =~ b 字符串匹配

  2. a !~ b 字符串不匹配

这里的 a 为字符串,b 为模式,例如:

  1. if str =~ " "
  2. echo "str contains a space"
  3. endif
  4. if str !~ ‘\.$‘
  5. echo "str does not end in a full stop"
  6. endif

字符串比较和匹配时会受到 ignorecase 选项的影响,你可以避免此选项的影响:

  1. 在操作符后添加 # 表示不忽略大小写

  2. 在操作符后添加 ? 表示忽略大小写
  1. if ‘hello‘ ==? ‘Hello‘
  2. echo ‘Here 1‘
  3. endif
  4. if ‘hello‘ ==# ‘Hello‘
  5. echo ‘Here 2‘
  6. endif

以上脚本输出 Here 1

另外,还有一个特别要注意的地方是:

  1. # 输出 matched
  2. if "a" =~ ‘\<‘
  3. echo ‘matched‘
  4. else
  5. echo ‘unmatched‘
  6. endif
  7. # 输出 unmatched
  8. if "a" =~ "\<"
  9. echo ‘matched‘
  10. else
  11. echo ‘unmatched‘
  12. endif

这里可以看到我们使用了不进行字符串转义的 ‘ 包裹字符串时得到了正确的结果,这是我们在使用正则表达式时特别需要注意的地方。

在使用 while 命令的时候,还可以配合 continue 和 break 命令使用,例如:

  1. while counter < 40
  2. call do_something()
  3. if skip_flag
  4. continue
  5. endif
  6. if finished_flag
  7. break
  8. endif
  9. sleep 50m
  10. endwhile

这里的 continue 命令用于跳转到循环的开始,break 命令用于结束循环。

execute 命令可以用于执行一个表达式,例如:

  1. let s:normal_command = ‘gg=G‘
  2. execute ‘normal ‘ . s:normal_command

这里的 normal 命令表示执行一个普通模式下的命令,注意到上面的 . 字符,其用于连接字符串。

eval 函数可以用于获取一个表达式的值,例如:

  1. let optname = "path"
  2. let optval = eval(‘&‘ . optname)

函数(Functions)

Vim 定义了很多函数,文档在这里:
http://vimdoc.sourceforge.net/htmldoc/usr_41.html#function-list
http://vimdoc.sourceforge.net/htmldoc/eval.html#functions

直接调用函数使用 call 命令:

  1. call search("Date: ", "W")

注意,区别函数和命令:

  1. search("Date: ", "W")

这样使用会出错,因为 search 是一个函数,而非命令。

我们可以自己定义函数:

  1. function {name}({var1}, {var2}, ...)
  2. {body}
  3. endfunction

注意,我们定义的函数的函数名必须为大写字母开头。我们来定义一个函数 Min 来获取两个数中较小的一个(此函数主要是为了解释语法,编写的并不优雅):

  1. function! s:Min(num1, num2)
  2. if a:num1 < a:num2
  3. let smaller = a:num1
  4. else
  5. let smaller = a:num2
  6. endif
  7. return smaller
  8. endfunction

首先需要意识到的是,函数也是变量,因此这里 s:Min 的作用域为定义函数的文件。function
命令后加上强制命令修饰符表示如果函数存在则替换,这样做很有必要,假定此函数位于某个脚本文件中,如果没有加上强制命令修饰符,脚本文件被载入两次时就会报错:函数已存在。

其次,我们看到在函数内使用 num1 时加上了 a: 前缀,这可以告诉 Vim 此为函数参数。之后我们使用了 let 命令定义了 smaller
变量,此变量是一个局部变量,不能在函数外访问。
最后,我们使用了 return 命令返回 smaller。如果函数未使用 return 命令或者
return 命令没有参数,那么函数返回 0。

删除一个函数使用命令 delfunction。直接使用 function 命令可以打印用户定义的函数,查看函数的定义使用:

  1. function Funcname

数据结构 List

一个 list 包含一组有序的元素,每个元素可以为任意类型,元素可以通过索引进行访问,第一个元素的索引为 0。创建一个 list,list
使用两个中括号包裹:

  1. " 创建一个空的 list
  2. let list1 = []
  3. " 创建一个 list,其中含有两个类型不同的元素
  4. let list2 = [‘a‘, 2]

list 相关的操作:

  1. 元素的访问
    1. let list[0] = 1
    2. echo list[0]

  2. 增加新的元素
    1. " 添加新的值到 list 的尾部
    2. call add(list, val)
    3. " 添加新的值到 list 的头部
    4. call insert(list, val)

  3. 删除元素
    1. " 删除索引为 index 的元素并返回此元素
    2. call remove(list, index)
    3. " 删除索引为 startIndex 到 endIndex(含 endIndex)的元素
    4. " 返回一个 list 包含了这些被删除的元素
    5. call remove(list, startIndex, endIndex)
    6. " 清空 list,这里索引 -1 对应 list 中最后一个元素
    7. call remove(list, 0, -1)

  4. 判断 list 是否为空
    1. if empty(list)
    2. " ...
    3. endif

  5. 获取 list 的大小
    1. echo len(list)

  6. 拷贝 list
    1. " 浅拷贝 list
    2. let copyList = copy(list)
    3. " 深拷贝 list
    4. let deepCopyList = deepcopy(list)
    5. call deepcopy()

  7. list 的遍历可以使用 for 命令
    1. let list = [‘one‘, ‘two‘, ‘three‘]
    2. for element in list
    3. echo element
    4. endfor

for 命令的用法如下:

  1. for {varname} in {listexpression}
  2. {commands}
  3. endfor

数据结构字典(Dictionaries)

Dictionary 是一个关联数组。每个元素都有一个 key 和一个 value,我们可以通过 key 获取到 value。创建一个
dictionary,dictionary 使用两个大括号包裹:

  1. " 创建一个空的 dictionary
  2. let dict = {}
  3. " 创建一个非空的 dictionary
  4. let dict = {‘one‘: 1, ‘two‘: 2, ‘three‘: 3 }

dictionary 的基本形式为:

  1. {<key> : <value>, ...}

dictionary 的相关操作:

  1. 元素的访问和修改
    1. let dict = {‘one‘: 1, ‘two‘: 2}
    2. " 通过 key 访问
    3. echo dict[‘one‘]
    4. " 当 key 为 ASCII 字符串时还可以这样访问
    5. echo dict.one
    6. " 修改元素的 value
    7. let dict[‘one‘] = ‘1‘

  2. 元素的增加和删除
    1. " 增加一个元素
    2. let dict[key] = value
    3. " 删除一个元素
    4. unlet dict[key]

  3. 获取 dictionary 大小
    1. echo len(dict)

  4. 使用 for 遍历一个 dictionary
    1. let dict = {‘one‘: 1, ‘two‘: 2}
    2. for key in keys(dict)
    3. echo key
    4. endfor
    5. " 遍历时 key 是未排序的,如果希望按照一定顺序访问可以这么做:
    6. for key in sort(keys(dict))
    7. " ...
    8. endfor
    9. " keys 函数用于返回一个 list,包含 dictionary 的所有 key
    10. " values 函数用于返回一个 list,包含 dictionary 的所有 value
    11. " items 函数用于返回一个 list,包含 dictionary 的 key-value 对
    12. for value in values(dict)
    13. echo value
    14. endfor
    15. for item in items(dict)
    16. echo item
    17. endfor

编写插件(plugins)

Vim 官方网站上存在大量的插件。现在来了解一下 Vim 插件。Vim
插件有两种类型:

  1. 全局插件:用于所有类型的文件

  2. filetype 插件:用于特定类型的文件(详细参考:http://vimdoc.sourceforge.net/htmldoc/usr_41.html#write-filetype-plugin

为了让你编写的插件能够总是正常工作,那么有一些规则需要遵循。

通常来 line-continuation 可以很好的工作:

  1. let str = ‘Hello‘
  2. \ . ‘World‘

但是如果设置了 compatible 则会出现错误。我们使用以下方式避免错误的发生:

  1. " 保存 cpoptions
  2. let s:save_cpo = &cpo
  3. " 设置 cpoptions 为 Vim 默认值
  4. set cpo&vim
  5. "
  6. " plugin content
  7. "
  8. " 还原 cpoptions
  9. let &cpo = s:save_cpo

插件的载入。有时候用户并不希望去载入某个插件,这时候我们需要给用户一种避免插件载入的机制:

  1. if exists("g:loaded_pluginname")
  2. " finish 用于避免 Vim 读取此文件剩余的部分
  3. finish
  4. endif
  5. let g:loaded_pluginname = 1
  6. "
  7. " plugin content
  8. "

这样,当我们在 vimrc 中设置了变量 g:loaded_pluginname 的值为 1
就避免了插件的载入。除了让用户可以控制是否能够载入插件之外,这样做还可以避免插件被重复的载入。另外,我们注意到 g:loaded_pluginname
的命名,这是建议的命名方式。

定义用户命令。编写插件时常常需要定义用户命令:

  1. " MyCommand 为命令名称
  2. " -nargs 用于指定命令参数的个数
  3. " s:funcName 为命令执行时执行的函数
  4. if !exists(‘:MyCommand‘)
  5. command -nargs=1 MyCommand :call s:funcName(<q-args>)
  6. endif

(转)Vim 脚本语言,布布扣,bubuko.com

时间: 2024-12-14 05:56:05

(转)Vim 脚本语言的相关文章

vim脚本语言

转自:http://man.chinaunix.net/newsoft/vi/doc/usr_41.html#usr_41.txt Vim 脚本语言在很多地方用到,包括 vimrc 文件, 语法文件, 等等.本章讨论 Vim 脚本 相关的知识.这样的内容有很多,所以本章也比较长. |41.1| 简介 |41.2| 变量 |41.3| 表达式 |41.4| 条件语句 |41.5| 执行一个表达式 |41.6| 使用函数 |41.7| 定义一个函数 |41.8| 异常 |41.9| 其它讨论 |41

vim脚本(一)

优秀的文本编辑器 有这样一则老笑话:如果 Emacs 拥有一款优良的文本编辑器,那么它将是一个优秀的操作系统,相反,如果 vi 拥有一个不错的操作系统,那么它将是一款非常出色的文本编辑器.这个笑话反映了 Emacs 有一个一直比 vi 好的策略优势:一种嵌入式扩展编程语言.实际上,Emacs 用户一直乐于忍受引入了 RSI 的控制问题,并且愿意在 Lisp 中编写他们的扩展,这个事实展示了内置扩展语言的强大优势. 现在,vi 程序员不再需要向 Emacs 的插入式脚本语言投去嫉妒的眼光.我们最喜

Ruby(面向对象程序设计的脚本语言)入门

Ruby是一种为简单快捷的面向对象编程(面向对象程序设计)而创的脚本语言. 简介 Ruby 是开源的,在Web上免费提供,但需要一个许可证. Ruby 是一种通用的.解释的编程语言. Ruby 是一种真正的面向对象编程语言. Ruby 是一种类似于 Python 和 Perl 的服务器端脚本语言. Ruby 可以用来编写通用网关接口(CGI)脚本. Ruby 可以被嵌入到超文本标记语言(HTML). Ruby 语法简单,这使得新的开发人员能够快速轻松地学习 Ruby. Ruby 与 C++ 和

哪种脚本语言最适合你!

本文译自 iSystemAdmin 的 <List Of Popular Scripting Languages for Linux and Windows>. 具备脚本知识的系统管理员与其他系统管理员有着明显的区别.脚本是一门“系统管理员”创造的艺术.这门艺术需要了解系统本身的相关知识,系统管理命令的语法,编程和算法知识以及至少一门脚本编程语言.对系统管理员来说编写脚本有很多选择,每种脚本语言都有着自己独特的语法和优点.脚本语言之间几乎没有相似之处,但也不会像外星语那样难以读懂.脚本语言既可

实现真正的自动化,expect脚本语言使用

实现真正的自动化,expect脚本语言使用 expect中的几个重要句子: expect的核心是spawn expect send set spawn 调用要执行的命令 expect 等待命令提示信息的出现,也就是捕捉用户输入的提示: send 发送需要交互的值,替代了用户手动输入内容 set 设置变量值 interact 执行完成后保持交互状态,把控制权交给控制台,这个时候就可以手工操作了.如果没有这一句登录完成后会退出,而不是留在远程终端上. expect eof 这个一定要加,与spawn

Linux和Windows脚本语言

本文译自 iSystemAdmin 的 <List Of Popular Scripting Languages for Linux and Windows>. 具备脚本知识的系统管理员与其他系统管理员有着明显的区别.脚本是一门“系统管理员”创造的艺术.这门艺术需要了解系统本身的相关知识,系统管理命令的语法,编程和算法知识以及至少一门脚本编程语言.对系统管理员来说编写脚本有很多选择,每种脚本语言都有着自己独特的语法和优点.脚本语言之间几乎没有相似之处,但也不会像外星语那样难以读懂.脚本语言既可

java脚本语言学习心得

第一篇技术博客,一定要认真! 第一篇技术博客,一定要认真! 第一篇技术博客,一定要认真! 好了,进入正题: 一 什么是脚本语言? 程序的运行方式有两种:编译运行和解释运行 1.1 前者的典型代表是java, 从文件角度看分为三步: write[编写]: a.java文件(拿个记事本就能写,扩展名是.java), compile[编译]: 编译(cmd命令是java a.java,ide集成了编译器运行之前自动编译)之后产生了a.class文件(是一堆二进制码,人看不懂,是给虚拟机看的) 运行[r

关于JS脚本语言的基础语法

JS脚本语言的基础语法:输出语法  alert("警告!");  confirm("确定吗?");   prompt("请输入密码");为弱类型语言: 开始时要嵌入JS代码:<script type="text/javascript"></script>: 关于写程序是需注意的基本语法:1.所有的字符全都是英文半角的:2.大部分情况下每条语句结束后要加分号:3.每一块代码结束后加换行:4.程序前呼后应:

shell、cmd、dos和脚本语言区别和联系

问题一:DOS与windows中cmd区别   在windows系统中,"开始-运行-cmd"可以打开"cmd.exe",进行命令行操作. 操作系统可以分成核心(kernel)和Shell(外壳)两部分,其中,Shell是操作系统与外部的主要接口,位于操作系统的外层,为用户提供与操作系统核心沟通的途径.在windows系统中见到的桌面即explorer.exe(资源管理器)是图形shell,而cmd就是命令行shell.这算是cmd与dos的最大区别,一个只是接口.