热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

如何编写EmacsScript

Emacs作为一款文本编辑器已经为大家所熟知,但是可能比较少人会想到它还能用来像python,ruby一样作为一门脚本语言来用.注意:EmacsScript的坑超级多,强烈推荐阅

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-alistcommand-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的执行顺序为:

  1. Emacs读取并执行EmacsScript中的内容
  2. Emacs遍历 command-line-args-left 中的参数,对于 command-switch-alist 中的参数调用对应的函数,对于不在 command-switch-alist 中的参数依次调用 command-line-functions 中的函数
  3. 倘若 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-processstart-process,这两个函数能让你更细致地控制子进程.

加速EmacsScript的启动过程

--script 选项会阻止Emacs启动时加载用户的初始化文件,但是依然会加载global site初始化文件.

若因此而拖慢了EmacsScript的启动速度,那么可以考虑添加 --quick 选项来明确禁止global site的初始化.


推荐阅读
  • PyQt5 QTextEdit:深入解析Python中多功能GUI库的应用与实现
    本文详细探讨了 PyQt5 中 QTextEdit 组件在 Python 多功能 GUI 库中的应用与实现。PyQt5 是 Qt 框架的 Python 绑定,提供了超过 620 个类和 6000 个函数及方法,广泛应用于跨平台应用程序开发。QTextEdit 作为其中的重要组件,支持丰富的文本编辑功能,如富文本格式、文本高亮和自定义样式等。PyQt5 的流行性不仅在于其强大的功能,还在于其易用性和灵活性,使其成为开发复杂用户界面的理想选择。 ... [详细]
  • 从 Java 过渡到 Ruby,不仅是一次编程语言的转换,更是一段技术进阶的旅程。本文将深入探讨两种语言在语法、生态系统和开发模式上的差异,帮助开发者顺利实现转型,并在新的环境中高效地编写高质量代码。 ... [详细]
  • Lunix历史及如何学习
    1.Lunix是什么1.1Lunix是操作系统还是应用程序Lunix是一套操作系统,它提供了一个完整的操作系统当中最底层的硬件控制与资源管理的完整架构, ... [详细]
  • Node.js 教程第五讲:深入解析 EventEmitter(事件监听与发射机制)
    本文将深入探讨 Node.js 中的 EventEmitter 模块,详细介绍其在事件监听与发射机制中的应用。内容涵盖事件驱动的基本概念、如何在 Node.js 中注册和触发自定义事件,以及 EventEmitter 的核心 API 和使用方法。通过本教程,读者将能够全面理解并熟练运用 EventEmitter 进行高效的事件处理。 ... [详细]
  • 深入解析:JavaScript中的表达式与语句有何不同
    深入解析:JavaScript中的表达式与语句有何不同 ... [详细]
  • Spring框架入门指南:专为新手打造的详细学习笔记
    Spring框架是Java Web开发中广泛应用的轻量级应用框架,以其卓越的功能和出色的性能赢得了广大开发者的青睐。本文为初学者提供了详尽的学习指南,涵盖基础概念、核心组件及实际应用案例,帮助新手快速掌握Spring框架的核心技术与实践技巧。 ... [详细]
  • 本文详细介绍了如何在Linux系统中搭建51单片机的开发与编程环境,重点讲解了使用Makefile进行项目管理的方法。首先,文章指导读者安装SDCC(Small Device C Compiler),这是一个专为小型设备设计的C语言编译器,适合用于51单片机的开发。随后,通过具体的实例演示了如何配置Makefile文件,以实现代码的自动化编译与链接过程,从而提高开发效率。此外,还提供了常见问题的解决方案及优化建议,帮助开发者快速上手并解决实际开发中可能遇到的技术难题。 ... [详细]
  • 本文详细介绍了在Ubuntu操作系统中使用GDB调试工具深入分析和调试标准库函数`printf`的源代码过程。通过具体步骤和实例,展示了如何设置断点、查看变量值及跟踪函数调用栈,帮助开发者更好地理解`printf`函数的工作原理及其内部实现细节。 ... [详细]
  • C语言实现:深入解析希尔排序算法
    C语言实现:深入解析希尔排序算法 ... [详细]
  • Python正则表达式详解:掌握数量词用法轻松上手
    Python正则表达式详解:掌握数量词用法轻松上手 ... [详细]
  • 手机上编写和运行PHP代码的最佳软件推荐 ... [详细]
  • 在Linux/WSL环境中,本文对Shell任务的并行处理进行了详细的测试与分析。通过多种并行处理技术,如GNU Parallel和xargs,探讨了如何有效提升任务执行效率和系统资源利用率。实验结果表明,合理配置并行参数能够显著缩短任务完成时间,提高系统整体性能。此外,文章还介绍了Shell脚本编写的基本原则和最佳实践,为读者提供了实用的参考。 ... [详细]
  • 网站前端开发的核心理念与必备技能解析 ... [详细]
  • https:www.hollischuang.comarchives74 对于Java程序员来说,null是令人头痛的东西。时常会受到空指针异常(NPE ... [详细]
  • rtemsapi用户指南Elixir代表了相对较新的编程语言,面向更广泛的受众。它于2011年发布,此后一直在开发中。他的主要特征是取消功能范式 ... [详细]
author-avatar
明依丞
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有