编写Common Lisp代码时,我使用SLIME。特别是,我使用C-C C-k编译包含函数定义的缓冲区,然后切换到REPL以运行这些函数。将可执行代码放在缓冲区中运行这些函数似乎不能很好地工作。如果代码有错误,它可能会弄得一团糟。
有一种方法可以包含不在缓冲区中编译的代码,但是可以从命令行运行,例如,做什么时
sbcl --script foo.lisp
如果是这种情况,每次我想从命令行运行代码时,我都不必继续添加和删除代码。是否存在这样的情况?
这类似于Python条件。
if __name__=='__main__':
如果将Python文件作为模块导入,则为false;如果将其作为脚本运行,则为true。
此博客文章名为"Using SBCL for Common Lisp shell scripting",由随机谷歌搜索找到,
;; If run from command line, the following if-test will succeed
(if (> (length sb-ext:*posix-argv*) 1)
;; Code that is executed from the command line only goes here
)
包含的代码确实不会被SLIME中的编译器运行,但它也不会由sbcl --script
运行。
更新:感谢Vsevolod Dyomkin的有用回答和后续跟进。 关于该答案的一些注释如下,从对该答案的评论中汇编而成。 @Vsevolod,如果你将这些添加到你的答案中,我会删除它们。
首先,我问了一种从命令行运行代码的方法,但不是从解释器运行代码。 提供的解决方案做得更多;它还提供了一种从解释器而不是命令行运行代码的方法。
第一步是为宏字符#!
定义reader macro function。
如链接“在遇到宏字符时,Lisp读者调用其读取器宏函数”中所述。
阅读器功能由对set-dispatch-macro-character
的调用定义。
因此,当看到#!
字符时,set-dispatch-macro-character
会导致调用正文中定义的lambda函数。
然后,此函数会将关键字:noscript
添加到*features*
变量中。
另请参阅讨论在SO问题中哪些读取器宏有用
Read macros: what do you use them for?
观察到:noscript
字符存在时,关键字*features*
会被添加到#!
。此外,#!
当代码在解释器内运行时存在字符,例如使用slime
时,但显然没有(删除)程序的
正在运行sbcl --script
的文字。因此,当代码在互操作程序中运行时,:noscript
会添加到*features*
,但在运行时不会{...}}
脚本。
我们现在使用内置读取器宏#-/#+
,正如Vsevolod所说,它的行为类似于C的#IFDEF/#IFNDEF
。他们检查符号
在*features*
。在这种情况下,#-:noscript
会检查是否缺少:noscript
,并#+:noscript
检查是否存在:noscript
。
如果满足这些条件,则运行相应的代码。要包装一段代码,可以使用
progn
是这样的:#-:noscript (progn <your code here>)
。
最后,在运行使用此功能的代码之前,需要调用set-dispatch-macro-character
。在sbcl
的情况下,可以放
它在初始化文件~/.sbclrc
中。注意这种方法不依赖于Common Lisp实现是SBCL。
如sbcl-devel列表中所提到的,更简单的替代方法是使用在:SWANK
中键入*features*
时出现关键字#-:swank
的事实
使用SLIME在emacs中的REPL。 SWANK是SLIME的服务器端。 SLIME应该更准确地称为SLIME / SWANK,就像这两个一样
客户端 - 服务器体系结构的客户端/服务器组件。我发现这篇名为Understanding
SLIME的博客文章很有帮助。
因此,可以像#+:swank
和#-:noscript
一样使用#+:noscript
和sbcl
,但不需要编写任何代码。
当然,如果使用命令行解释器:SWANK
,这将无法工作,例如,
从那时起,*features*
将不会出现在{{1}}。
答案 0 :(得分:13)
您可以使用以下技巧:
为shebang定义一个调度函数:
(set-dispatch-macro-character #\# #\!
(lambda (stream c n)
(declare (ignore c n))
(read-line stream)
`(eval-when (:compile-toplevel :load-toplevel :execute)
(pushnew :noscript *features*))))
在您的脚本文件中使用#-:noscript
:
#!/usr/local/bin/sbcl --script
(defun test (a) (print a))
(test 1)
#-:noscript (test 2)
#+:noscript (test 3)
执行./test.lisp
将打印1和2,而 C-c C-k 将输出1和3.
<强> EDITS 强>
这个技巧应该有效,因为当通过SLIME或其他机制加载文件时,sbcl --script
完全删除了shebang行,但是没有删除。
这种方法的缺点是我们在特征中缺少:noscript
而不是:script
的存在。要修改它,应在sbcl --script
处理本身中推送相应的功能。
答案 1 :(得分:4)
Fare Rideau编写了一个漂亮的unix实用程序CL-Launch,它可以从命令行执行lisp软件。它集成了Quicklisp支持,适用于大多数Common Lisp实现。
示例脚本可能如下所示:
#!/usr/bin/cl -sp "hunchentoot" -Q -E main
(defun main (argv)
(format t "~A: ~{~A ~}~%" (truename ".") argv)
(hunchentoot:start
(make-instance 'hunchentoot:acceptor
:document-root (truename ".")
:port 8080))
(do ((x #\s (read-char)))
((char-equal x #\q) nil)))
向脚本添加+ x权限并调用它后,它将在当前目录中启动http服务器。 “-sp”标志表示要加载的包,因此从包中抽象shell脚本是相当简洁的方法。
有关详细信息,请参阅: http://cliki.net/CL-Launch
答案 2 :(得分:1)
我有同样的问题,我偶然发现了这个问题。至少对于sbcl,似乎我可以使用(sb-ext:posix-getenv "_")
。当在slime中运行时,它返回/usr/bin/emacs
(或者emacs的路径),否则返回我用来调用脚本的命令。因此,总是可以区分粘液和脚本调用,直到您成为emacs的贡献者:)
如果要获取要调用的脚本的完整路径名,可以使用(truename (sb-ext:posix-getenv "_"))
。但是,当从slime运行时,它将返回有效的emacs路径名,例如, /usr/bin/emacs-24.5
,所以这可能不太方便。