在Common Lisp中是否存在运行外部程序的标准方式?

13
在clisp中,以下的代码是有效的:
(defun hit-history () (shell "tail ssqHitNum.txt"))

然而,在Clozure CL中,shell函数不被支持!

6个回答

12
没有标准方法,但有一些库可以为重要的实现提供此功能。例如,在Quicklisp中提供了trivial-shell,它提供了shell-command。(我实际上没有测试过,但它是CLiki上的recommended libraries之一。)还有external-program。更新:inferior-shell似乎是这些天更受欢迎的选择,正如Ehvince在评论和他自己的答案中指出的那样。
您还可以使用读取时间条件来使不同的实现使用其各自的功能来完成此操作。
例如,CCL有ccl:run-program
CL-USER> (run-program "whoami" '() :output *standard-output*)
foobar
#<EXTERNAL-PROCESS (whoami)[NIL] (EXITED : 0) #xC695EA6>

请注意,您使用优秀的 trivial-shell 库无需快速加载 Quicklisp。此外,我不确定“读取时间条件”的建议背后的原因,因为这正是 trivial-shell 所做的。这就是为什么这些库被称为 trivial-* 的原因。 - Ken
3
是的,Ken,你不需要使用Quicklisp,但这是目前安装Lisp库通常推荐的方式。如果您想自己安装,请随意添加自己的答案来解释如何手动安装。我提到read-time-conditionals,是因为发帖者可能出于某些原因不想使用库,此外,如果没有类似问题的trivial-*,也知道这很有用。(当然,read-time conditionals也有其他用途,所以了解它们也很好。) - danlei
trivial-shell已被弃用,现已被inferior-shell所取代,它简单地使用uioprun-program(同步)。如需异步,请参阅uiop的launch-program - Ehvince
@Ehvince 如果你认为我的回答因此应该被踩,那就这样吧。尽管如此,它仍然正确地回答了实际问题。 - danlei
@daniel 我希望新的答案能够得到更多的投票,但是可能downvote并不是解决方案,抱歉。我取消了它(尤其是你编辑了你的问题之后)。 - Ehvince
@Ehvince 我明白了。就我个人而言,只有当答案真的是错误的或者质量非常差的时候,我才会给它点踩。当我更喜欢另一个答案而不是被采纳的答案时,我通常只会点赞并且不去碰被采纳的答案。但我同意,当语言或生态系统发生变化时,如果你看到一个过时的答案排在前面,而更好的答案却缺乏可见性,这确实是一个问题。无论如何,我很高兴你留下了评论让我知道发生了什么。我真正讨厌的是那些让我毫无头绪的踩。同时,感谢你撤销了踩。 - danlei

8

是的,使用ASDF的一部分UIOP,应该包含在所有现代实现中。

  • 同步命令:uiop:run-program
  • 异步命令:uiop:launch-program

例如:

(uiop:run-program (list "firefox" "http:url") :output t)

或者

(defparameter *shell* (uiop:launch-program "bash" :input :stream :output :stream))

这里是您可以发送输入并读取输出的地方。

它们在此得到更详细的解释:https://lispcookbook.github.io/cl-cookbook/os.html#running-external-programs

trivial-shell已经过时,被inferior-shell替代,后者内部使用可移植的uioprun-program(同步方式),因此我们只需使用它即可。


2
(defun dot->png (fname thunk)
   (with-open-file (*standard-output*
       fname
       :direction :output
       :if-exists :superseded)
     (funcall thunk))
   (ccl:run-program "dot" (list "-Tpng -O" fname))
)

我在学习《Lisp之地》的第123页时,在ccl(Clozure)中成功运行了程序。


当我阅读《Lisp之国》相同的段落时,这对我很有帮助。我正在使用SBCL,并且不得不将最后一行更改为(sb-ext: run-program "/usr/bin/dot" (list "-Tpng" "-O" fname))) - dpritch

1
请查看 inferior-shell 包。 (通过万能的 quicklisp 包管理器获取。)
如果您有互联网,则此功能在解释器中可用。
(require 'inferior-shell)
(inferior-shell:run/s '(curl icanhazip.com))

1
以下是在Common Lisp中调用wget的示例:

https://diasp.eu/posts/1742240

这里是代码:

(sb-ext:run-program "/usr/bin/wget" '("-O" "<path-to-output-file>" "<url-link>") :output *standard-output*) 

请将相关代码片段复制到答案中,以防止链接失效,从而使答案无用。 - EWit

0

CL21 定义了简单的方法:

(in-package :cl21-user)
(use-package :cl21.process)

然后使用run-process或者#`阅读器宏:

(run-process '("ls" "-l"))
;-> total 0
;   drwxrwxrwt    5 root         wheel   170 Nov  1 18:00 Shared
;   drwxr-xr-x+ 174 nitro_idiot  staff  5916 Mar  5 21:41 nitro_idiot
;=> #<PROCESS /bin/sh -c ls -l /Users (76468) EXITED 0>

或者

#`ls -l /Users`
;=> "total 0
;   drwxrwxrwt    5 root         wheel   170 Nov  1 18:00 Shared
;   drwxr-xr-x+ 174 nitro_idiot  staff  5916 Mar  5 21:41 nitro_idiot
;   "
;   ""
;   0

这个源代码展示了针对SBCL和CCL的实现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接