如何编写EmacsScript
来源:互联网 发布:湖北广电网络官网 编辑:程序博客网 时间:2024/06/03 12:18
Emacs作为一款文本编辑器已经为大家所熟知,但是可能比较少人会想到它还能用来像python,ruby一样作为一门脚本语言来用.
注意: EmacsScript的坑超级多,强烈推荐阅读这篇文章:emacs-script-pitfalls (这里是它的中文版emacs-script中的那些坑)
–script选项
Emacs提供了一个 --script
选项可以让Emacs运行在batch模式下,并运行指定文件中的elisp代码.
在batch模式下emacs完全作为一个elisp语言解释器来运行,并在执行完所有的elisp代码后直接退出. 在elisp代码执行期间,那些输出到echo area中的内容会输出到stdout或stderr中,那些从minibuffer读取内容的函数会变成从stdin读取内容.
Emacs为了遵循shell script的shebang标准,特意将第一行内容中的的 #!
当成注释符号来处理,因此你的EmacsScript一般会是这样的:
#!/usr/bin/emacs --script(message "Hello world")
当然,这种写法并不具有可移植性的,毕竟不是所有的emacs路径都是 /usr/bin/emacs
. 真正具有可移植性的写法应该是:
#!/bin/sh":"; exec emacs --script "$0" "$@" # -*- mode: emacs-lisp; lexical-binding: t; -*-(message "Hello world")
处理命令行参数
注意:对于EmacsScript来说,参数与选项是截然不同的两个东西. 而且选项不能放在参数最后,否则Emacs会提示”emacs: Option ‘-f’ requires an argument”
在EmacsLisp中,与处理命令行参数有关的常用变量有这么几个:
command-line-args-left
尚未处理的command-line argument列表.
假设有这么一个”/tmp/test1.el”的脚本:
#!/bin/sh":"; exec emacs --script "$0" "$@" # -*- mode: emacs-lisp; lexical-binding: t; -*-(princ (format "command-line-args-left=%s" command-line-args-left))
那么执行该脚本的结果是:
/tmp/test1.el a b c
command-line-args
传递给Emacs的完整command-line argument列表,但是这个变量一般很少用,但它可以用于获取script脚本本身的名字.
假设有这么一个”/tmp/test2.el”的脚本:
#!/bin/sh":"; exec emacs --script "$0" "$@" # -*- mode: emacs-lisp; lexical-binding: t; -*-(princ (format "command-line-args=%s\n" command-line-args))(princ (format "$0=%s" (nth 2 command-line-args)))
那么执行该脚本的结果是:
/tmp/test2.el a b c
可以用
(nth 2 command-line-args)
来获取脚本名称.command-switch-alist
Emacs在执行完EmacsScript中的语句之后,会检查
command-line-args-left
中是否包含有以-
开头的选项,并在该变量中查找并运行对应的handler-function. 每处理完一个选项之后,就将该参数从command-line-args-left
中删除掉.该变量是元素为`(option . handler-function)’的alist. 这里
option为command-line argument中的`-option’参数(带-),为字符串格式
handler-function为相应的处理函数名,它接收option为唯一参数
若command line option后还带了其他参数,则在handler-function中可以通过变量`command-line-args-left’来获取剩余的命令行参数.
例如有这么一个脚本:
#!/bin/sh":"; exec emacs --script "$0" "$@" # -*- mode: emacs-lisp; lexical-binding: t; -*-(defun print-option-value (option) (princ (format "command-line-args-left=%s\n" command-line-args-left)) (princ (format "value of %s is %s\n" option (car command-line-args-left))) (pop command-line-args-left))(add-to-list 'command-switch-alist '("-f" . print-option-value))
那么执行该脚本的结果是:
/tmp/test3.el a -f filename b c
command-line-functions
该变量是一系列函数的列表,这些函数用来处理无法识别的command-line参数.
每次处理一个没有特殊意义的command line argument时,该变量中的函数都会被依次调用, 直到有一个函数返回非nil的值
这些函数被调用时并不传递参数,但在这些函数内可以通过变量`argi’获取当前待处理的command-line argument. 可以通过变量`command-line-args-left’获取尚未被处理的command line arguments.
若某函数除了当前待处理的函数,同时也把后面的参数給处理过了,则需要把后面那些被处理过的参数从`command-line-args-left’中删除
若某函数已经处理了当前代处理的参数,则一定记得返回非nil值. 若所有的函数都返回nil,该参数会被认为是Emacs要打开的文件名称
例如有这么一个脚本:
#!/bin/sh":"; exec emacs --script "$0" "$@" # -*- mode: emacs-lisp; lexical-binding: t; -*-(defun print-option () (princ (format "command-line-args-left=%s\n" command-line-args-left)) (princ (format "option is %s\n" argi)))(add-to-list 'command-line-functions #'print-option)
那么执行该脚本的结果是:
/tmp/test4.el a -p filename b
我们可以在脚本中同时使用
command-switch-alist
与command-line-functions
. 它们的调用顺序是按照传递给EmacsScript的参数顺序来进行的.例如有这么一个脚本:
#!/bin/sh":"; exec emacs --script "$0" "$@" # -*- mode: emacs-lisp; lexical-binding: t; -*-(defun print-option () (princ (format "option is %s\n" argi)))(add-to-list 'command-line-functions #'print-option)(defun print-option-value (option) (princ (format "value of option %s is %s\n" option (pop command-line-args-left))))(add-to-list 'command-switch-alist '("-f" . print-option-value))
那么执行该脚本的结果会是:
/tmp/test5.el a -f f -p p
EmacsScript的执行顺序
从上面命令行参数的说明中,大致可以推断出EmacsScript的执行顺序为:
- Emacs读取并执行EmacsScript中的内容
- Emacs遍历
command-line-args-left
中的参数,对于command-switch-alist
中的参数调用对应的函数,对于不在command-switch-alist
中的参数依次调用command-line-functions
中的函数 - 倘若
command-line-functiions
中没有定义函数,或者某参数在依次调用command-line-functions
中的函数后所有函数都返回nil的话,那么该参数交由emacs本身处理.
标准输出,标准错误与标准输入
在interactive模式下编写EmacsLisp函数时,我们习惯于用 message
函数来输出内容,然而在batch模式下,我们就不能再用 message
来输出内容了,因为 message
实际上会把内容输出到stderr上.
作为替代,若是要想将内容输出到stdout,你需要使用 print, prin1, princ 等这一系列的函数来输出内容. 然而这一类的函数本身并没有格式化输出的功能,因此你一般还需要用 format
函数预先将要输出的内容格式化成字符串.
那么如何从标准输入读取内容呢? 只需要跟interactive模式下一样使用 read-xxx
系列函数就行了. 在batch模式下,原先从minbuffer读取内容的函数会改成从stdin中读取内容.
唯一需要注意的是:Emacs24及其之前的版本的Emacs在batch模式下用 read-passwd
从标准输出读取密码时,会在终端上显示出密码的内容. Emacs25版本的 read-passwd
则解决了这个问题.
获取外部命令的运行结果
在shell编程中,可以使用 $()
来捕获命令的运行结果, EmacsScript不支持这种语法,但可以通过函数 shell-command-to-string
来代替. 比如
假设有这么一个脚本:
#!/bin/sh":"; exec emacs --script "$0" "$@" # -*- mode: emacs-lisp; lexical-binding: t; -*-(princ "捕获ls的内容:\n")(princ (shell-command-to-string "ls -l"))
那么执行该脚本的结果是:
/tmp/test6.el
当然,如果你愿意,完全可以使用底层的 call-process
与 start-process
,这两个函数能让你更细致地控制子进程.
加速EmacsScript的启动过程
--script
选项会阻止Emacs启动时加载用户的初始化文件,但是依然会加载global site初始化文件.
若因此而拖慢了EmacsScript的启动速度,那么可以考虑添加 --quick
选项来明确禁止global site的初始化.
- 如何编写EmacsScript
- 如何编写?
- 如何编写屏保
- 如何编写PHP扩展
- 如何编写网络监视器
- 如何编写网络监视器
- 如何编写技术白皮书
- 如何编写Loader
- 如何编写Loader
- 如何编写DLL文件
- 如何编写测试计划?
- 如何编写DLL文件
- 如何编写Makefile(2)
- 如何编写makefile
- 如何编写病毒
- 如何编写网络监视器
- 如何编写PHP扩展
- 如何编写Filter
- 后期的预告
- C#一些基础语法总结
- js中关于scrolltop.offsettop等距离用法的介绍
- 一点就通:学会dpkg 命令管理 Debian 系的 Linux系统
- Tr A(矩阵快速幂)
- 如何编写EmacsScript
- C语言字符串处理
- map用迭代器进行遍历的方法总结
- 十大滤波算法
- gradle依赖
- Laravel5.3开发API(Dingo+Passport+Swagger)
- 使用tput创建屏幕输出
- 使用 Smartmontools 检测硬盘坏道
- Mybatis-基本配置文件