REPL是一个Clojure环境,它不能直接调用命令行/shell里面的命令,现在我们想在REPL里面实现一个shell,让它既可以像在shell里面直接对系统(或shell的内置命令)进行调用,也可以做REPL下能做的事情。
上一篇中我们写了一个read程序,这个程序会要求用户输入,并将获得的输入作为一个字符串来返回:
(defn ask
"Ask for an input."
([] (ask "Please Input"))
([^String prompt]
(printf "%s: " prompt)
(flush)
(.readLine *in*)))
有了这个函数,我们就可以装模做样地让REPL看起来像一个shell:
(defn shell[]
(let [prompt "shell->"
exit-cmd "let me out!"]
(loop [cmd (ask prompt)]
(if (= cmd exit-cmd)
(println "get out!")
(do
(exec cmd)
(recur (ask prompt)))))))
我们先假设其中的”exec“函数已经存在,定义好了这两个函数之后,直接在REPL里面调用”(shell)"就可以看到类似于以下的画面:
#'user/shell
user=> (shell)
shell->: testing
you said: testing
shell->: I want money
you said: I want money
shell->: oh....
you said: oh....
shell->: let me out!
get out!
nil
user=>
这里的"shell“函数我仅仅是用了println来代替里面的exec,整个函数现在看起来就像个应声虫,也许改名为echo会适当一些——但这都仅仅是因为我们还没实现”exec“函数而已。
当然我们这个shell天生有一个缺陷,就是只能读入一行的输入,这跟REPL不一样,在REPL里面如果被判断为未完成输入的话会一直等待输入结束(例如,必须有匹配的闭括号)。关于这个问题,我们后面也许会探讨出解决方法。
至于exec的实现,请读者自行实现,或者有缘的话,让我们期待下一篇……