Clojure:如何使用管道执行shell命令?

13

我发现使用 (use '[clojure.java.shell :only [sh]]) 命令可以在 Clojure 中执行 shell 命令。虽然 (sh "ls" "-a") 命令可以正常工作,但是 (sh "ls" "-a" "| grep" "Doc") 命令就不行了。有什么技巧吗?

1个回答

23

clojure.java.shell/sh执行一个命令(作为sh函数传递的第一个参数),并指定参数(作为传递给sh的其余参数)。

当你执行:

(sh "ls" "-a" "| grep" "Doc")

你请求执行带有参数-a| grepDocls

当您在终端中键入ls -a | grep Doc时,shell会将它解释为执行ls,获取其标准输出,并将其作为标准输入传递给另一个进程(grep),而该进程应该由shell启动。

你可以通过启动ls作为一个进程,获取它的标准输出,然后执行grep,并将来自ls的输出作为它的输入来模拟shell正在执行的操作。

更简单的解决方案是,只需请求一个shell进程来执行所有操作,就好像在终端中输入一样:

(sh "bash" "-c" "ls -a | grep Doc")

重要的是要将-cls ...作为单独的参数传递,以便bash将它们作为单独的参数获取。您还需要将要执行的整个命令作为一个字符串(ls -a | grep Doc)。否则,只有在-c之后的第一个参数将被视为命令。例如,下面的内容不能达到你的期望:

(sh "bash" "-c" "ls -a" "|" "grep Doc")


1
这是一个非常清晰明了的答案,谢谢!现在只是为了纯粹的知识,是否有一种方法可以通过实际发送 (":out (sh "ls" "-a")) 的输出到另一个 (sh "grep" "Doc") 来完成它? - shakedzy
2
你可以使用 sh:in 选项来传递前一个进程的 :out 值。但是,使用它会失去从一个进程流式传输数据的功能 - 你将缓冲一个进程的所有输出,当它完成时,你将把所有内容传递给另一个进程。Shell 使用管道,其中数据从一个进程流式传输到另一个进程,无需缓冲所有内容。你可以使用 java.lang.Runtime.exec()java.lang.Process.getOutputStream 来获取第一个进程的输出数据并将其传递给下一个进程,但 Clojure 的 sh 不支持它。 - Piotrek Bzdyl
4
您可以查看https://github.com/Raynes/conch。它支持管道:https://github.com/Raynes/conch#piping。 - Piotrek Bzdyl

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