突发奇想:emacs能否像python解释器一样地使用呢?这就让emacs在实际工作中有大量用武之地了!这个有可能吗?

python+包的模式,可在命令行独立运行脚本,可输出到标准输出,从标准输入读取,让emacs可以替换python。

1 个赞
* Startup Changes in Emacs 29.1

+++
** Emacs can now be used more easily in an executable script.
If you start an executable script with

    #!/usr/bin/emacs -x

Emacs will start without reading any init files (like with '--quick'),
and then execute the rest of the script file as Emacs Lisp.  When it
reaches the end of the script, Emacs will exit with an exit code from
the value of the final form.

新建一个文件,起个名字,假设就叫 tmp,输入内容

#!/usr/local/bin/emacs -x                                                                                                           
(message "hello, world: %s" (read-from-minibuffer "输入些什么:")) 

保存,关闭。然后执行 chmod +x tmp 给 tmp 文件添加执行权限,之后执行 ./tmp byzanz_record

27 个赞

simple

Amaaaaaaaazing!!! 我馋这样的实现好久了,真的,作为一个编程纯小白,能用到这样的功能真的巨开心! :laughing:

用了4年26.3 现在已经这么先进了吗

29 之前的版本用 --script 参数就可以了

#!/usr/local/bin/emacs --script                                                 
(message "hello, world: %s" (read-from-minibuffer "输入些什么:"))    

@aeghn 指正,考虑到可移植性性的话可以采用下面这个(因为 emacs 在不同的系统下安装的路径可能不一样):

对了,执行脚本时如果希望给脚本传入额外的参数的话,直接 ./tmp a b 就可以了,参数的值默认保存在 argv 变量里,这个变量是一个列表。脚本的 exit code 是最后一个 form 的值,可以自己试验一下:

#!/usr/bin/env -S emacs -x                                                                                                                                                
(message "hello, world %s: %s" argv (read-from-minibuffer "输入些什么:"))                                                                                                
2 

下面是在我电脑里的测试:

./tmp a b
输入些什么:哈哈哈
hello, world (a b): 哈哈哈
echo $?
2
3 个赞

这里是作为脚本啊,read-from-minibuffer 只是一个用来读取用户输入的函数,不是什么命令行版,也不是用来验证点子用的,而是将 Emacs Lisp 当作 Python 那样的脚本语言使用。

具体到脚本而言,跟在 emacs-lisp-mode 下编辑 Emacs Lisp 没什么区别。

可能我理解错了。

我想表达的是,如果想像 python -i 或 ipython 那样用,光 read-eval-print 还不行,还得配个好的编辑器。

如果是想用来写 shell 脚本或工具,请忽略我前面的回贴。

这个真不错啊,就是必须要29,还得等等。

shebang 里当然要用 env

比如 bash 可以改为

#!/usr/bin/bash

echo 1

就改为

#!/usr/bin/env bash

echo 1

那么这里

#!/usr/bin/emacs -x

就应该改为

#!/usr/bin/env -S emacs -x

请大家参考:

2 个赞

先前手机打字不便,来电脑上做些补充。

用 Elisp 写命令行工具其实有不少相当完备的例子和辅助工具:

  • Cask: Elisp 项目管理工具,package 开发必备。

    • ert-runner: Opinionated Ert testing workflow (Cask 插件)。
    • github-elpa: Build and Publish Your Own ELPA Repositories with GitHub Pages (Cask 插件)。
  • Eldev: 另一款不错的 Elisp 项目管理工具。

  • ecuckes: 集成测试工具 Cucumber 的 Elisp 移植。

  • epm: 论坛大佬 xuchunyang 写的包管理工具。

  • bin/doom DoomEmacs 的命令行工具。

  • commander: 命令行参数解析库:

    (commander
     (name "my-awesome-program")
     (option "--help" "Show usage information" commander-print-usage))
    

前面提到的文章《emacs-script中的那些坑》总结得并不全面,综合《想到一种启动 Emacs Script 的方法》可知:

  1. --script 并未禁止所有的初始化动作,还得加 -Q/--quick 参数

  2. #!/usr/bin/env emacs -Q --script 无法在 Linux 下使用,因为 Linux 下 shebang 不会拆分[1](Mac 下没问题)(:warning:#15 说加 -S 参数可解)。 29 的 -x 就是为了解决这个问题[2],并未增加什么实际的功能:

    $ emacs --help | rg '^-x' -A2
    -x                          to be used in #!/usr/bin/emacs -x
                                  and has approximately the same meaning
                                  as -Q --script
                                  ^^^^^^^^^^^^^^
    
  3. #!/usr/bin/env emacs --script 还有个问题,就是参数会被 Emacs 先拦截掉,例如:

    $ cat ./bin/hello.el
    #!/usr/bin/env emacs --script
    (message "Hello World!")
    
    $ ./bin/hello.el --version
    GNU Emacs 29.0.50                                          ⎫
    Copyright (C) 2022 Free Software Foundation, Inc.          |
    GNU Emacs comes with ABSOLUTELY NO WARRANTY.               ⎬ !!!
    You may redistribute copies of GNU Emacs                   |
    under the terms of the GNU General Public License.         |
    For more information about these matters, see the file ... ⎭
    
  4. ":"; exec emacs --quick --script "$0" -- "$@" # -*- mode: emacs-lisp; lexical-binding: t; -*- 可以解决参数问题,但它里边的 lexical-binding: t 不起作用:

    $ cat ./bin/hello.el
    #!/bin/sh
    ":"; exec emacs --quick --script "$0" -- "$@" # -*- mode: emacs-lisp; lexical-binding: t; -*-
    (message "Hello World!")
    (message "arguments: %S" command-line-args-left)
    (message "lexical-binding: %s" lexical-binding)
    
    $ ./bin/hello.el --help
    Hello World!
    arguments: ("--" "--help")
    lexical-binding: nil
                     ^^^
    

如果不是非得用单个文件实现,可以不必拘泥于 shebang 魔法或是等 29 的 -x。用 shell script + elisp 也能很好的实现(例如上面提到的 Cask/Eldev/ecuckes/epm),还能避开上述一些问题,我的一款即将完工的命令行工具也是采用这种模式。


6 个赞

补充一个: emacs-eask/eask: Command-line tool for building and testing Emacs Lisp packages (github.com)。我也是最近才知道的,作者集成到了lsp-pyright中。

1 个赞

哈哈哈。“cucks” 这个词有一个非常具体的含义。应该是"ecukes".

1 个赞

加个参数 -S 就可以了,这也是我删掉上面的链接的原因所在:

#!/usr/bin/env -S emacs -Q --script

我手上没有 Linux 无法验证,但的确搜到不少加 -S 带多个参数的源码,例如:

关于 shebang 不拆分的最初说法可能来自这个 2015 年的一个帖子[1](#12楼也有链接):

the “#!” implementation on most Unices will not be able to pass more than one argument to the interpreter.

讨论中也提到了 -x 参数[2]

Amazingly, “-x” is available as an Emacs command-line switch…

讨论中还给出了 shebang 参数分割测试的例子[3]

#!/bin/interpreter -1 -2 -3
# .. more script

$ ./script

OSX, FreeBSD:
argv[0] = "/bin/interpreter"
argv[1] = "-1"
argv[2] = "-2"
argv[3] = "-3"
argv[4] = "./script"

Linux, Cygwin
argv[0] = "/bin/interpreter"
argv[1] = "-1 -2 -3"
argv[2] = "./script"

时过境迁,有些出入情有可原。