Emacs shell 脚本 - 如何将初始选项放入脚本中?

Posted

技术标签:

【中文标题】Emacs shell 脚本 - 如何将初始选项放入脚本中?【英文标题】:Emacs shell scripts - how to put initial options into the script? 【发布时间】:2011-09-08 11:04:56 【问题描述】:

受堆栈溢出问题Idomatic batch processing of text in Emacs? 的启发,我尝试了一个带有以下标题的 Emacs shell 脚本:

#!/usr/bin/emacs --script 

我在其中放入了一些 Emacs Lisp 代码,并将其保存为文本文件 rcat。

由于 --script 选项不会阻止加载站点启动文件,所以我有很多

Loading /etc/emacs/site-start.d/20apel.el (source)...
Loading /etc/emacs23/site-start.d/35elib-startup.el (source)...
Loading /etc/emacs23/site-start.d/50auctex.el (source)...

Bash shell (stdout) 中的消息。我可以通过调用来防止这种情况发生

rcat --no-site-file

rcat -Q

但不是通过更改脚本中的标题:

 #!/usr/bin/emacs --script --no-site-file

有没有办法在这样的脚本文件中向 Emacs 传递额外的选项,而不是稍后在命令行中执行?

【问题讨论】:

如果您仍在阅读,我建议将此处接受的答案更改为 Gilles'。我认为这是对这个问题的最佳答案。我自己很快就采用了它作为任何 elisp 脚本的“标准”样板,所以 10 年后我的答案在这里仍然带有绿色勾号,这感觉很奇怪:) 【参考方案1】:

您可以随时将#!/usr/bin/emacs 更改为#!/home/thorsten/bin/emacs-no-site-file,并将其设置为:

#!/bin/sh
exec /usr/bin/emacs --no-site-file "$@"

如果这对您不起作用,或者如果您希望您的脚本是可移植的,那么请参阅 Gilles' answer below 以获得更强大(并且有点时髦:) 的方法。

【讨论】:

这行得通,谢谢。我总是忘记,计算机科学中几乎所有问题的解决方案都是在中间插入一个额外的(抽象)步骤;) 不幸的是,并不是所有的 unice 都接受链式解释器(即一个脚本不能是另一个脚本的解释器)。【参考方案2】:

许多 unix 变体只允许 shebang 行上的程序有一个参数。悲伤,但真实。如果您使用#!/usr/bin/env emacs 以便不依赖于emacs 可执行文件的位置,则根本无法传递参数。

Chaining scripts 在某些系统上是可能的,但并非所有系统都支持。

您可以采用历史悠久的方式编写多语言脚本:既是 shell 脚本又是 Emacs Lisp 脚本的脚本(例如 Perl 的 if $running_under_some_shell)。它确实看起来很老套,但它确实有效。

Elisp cmets 以; 开头,它在 shell 中分隔两个命令。所以我们可以使用; 后跟一个shell 指令来切换到Emacs,实际的Lisp 代码从下一行开始。大多数 shell 不喜欢空命令,所以我们需要找到一些 shell 和 Emacs 都视为无操作的东西,放在; 之前。 shell no-op 命令是:;就 shell 而言,你可以写成 ":",Emacs 将其解析为顶层的常量,这也是一个无操作。

#! /bin/sh
":"; exec emacs --no-site-file --script "$0" -- "$@" # -*-emacs-lisp-*-
(print (+ 2 2))

【讨论】:

太棒了!为什么每个人都对黑客和黑客如此挑剔?软件工程师可能找不到像这个一样有创意的解决方案。 交叉引用下面的 answer+comment,它展示了如何将 -- 参数合并到此方法中,以防止 Emacs 尝试处理传递给您的脚本的命令行参数 ***.com/questions/6759606/… @DiegoSevilla 该空行来自print 函数,该函数在对象前后打印一个换行符。使用prin1princ 避免换行。 看起来一个通用的解决方案仍然需要在脚本退出之前(setq argv nil)(根据 Jurijs Oniscuks 的回答),因为 Emacs 会将任何剩余的参数作为要访问的文件名处理。见***.com/a/24352935/324105 我还会留下一个指向 ***.com/a/10211087/324105 的链接,它收集了用于编写 elisp 脚本的关键样板 (IMO),基于此和其他问答。【参考方案3】:

如果您在脚本模式下运行 emacs,我建议您在脚本末尾将“argv”变量重置为 nil,否则 emacs 将在脚本完成后尝试解释“argv”。

假设您有一个名为“test-emacs-script.el”的文件,其内容如下:

#!/usr/bin/emacs --script
(print argv)
(setq argv nil)

尝试将此脚本作为“./test-emacs-script.el -a”运行。如果您在不重置“argv”(脚本的最后一行)的情况下运行此脚本,则输出将是:

("-a")
Unknown option `-a'

重置“argv”会消除“未知选项”错误消息

【讨论】:

即使使用了 -- 参数,这看起来也很重要,因为 lunaryorn points out in another Q&A Emacs 仍会将非选项参数作为文件名处理,如果有的话。清空 argv 可以防止这种情况发生。 更好的是,如果你(kill-emacs 0) 以指定的状态显式退出,Emacs 不会做任何事情。来自lunaryorn.com/2014/08/12/emacs-script-pitfalls.html 的这个技巧(我认为这已经成为一个非常全面的概述)。

以上是关于Emacs shell 脚本 - 如何将初始选项放入脚本中?的主要内容,如果未能解决你的问题,请参考以下文章

ubuntu12.04中用emacs进行shell编程怎么配置呢?

重播Emacs中击键的“脚本”

如何在 Shell 脚本中执行语法检查调试模式

shell脚本调试方法

Emacs shell 中的屏幕:如何分离?

如何调试LoadRunner脚本