Clojure shell / sh不能正确处理单引号的参数?

时间:2016-07-14 16:58:29

标签: bash shell clojure ffmpeg

在REPL中执行以下命令:

(shell/sh "ls" "-lah" "'resources'")

给出以下输出:

{:exit 2, :out "", :err "ls: cannot access 'resources': No such file or directory\n"}

在Bash shell中执行相同的命令会提供正确的输出,即resources目录中的文件列表。据我所知,这意味着shell / sh无法正确处理单引号参数。这是真的还是我做错了什么?

上面的示例是一个简单的示例,因为通常我可能不需要单引号文件夹名称。但为什么这是一个真正的问题是因为我尝试通过使用shell / sh执行以下ffmpeg命令来连接音频文件;

ffmpeg -i resources/ffmpeg_working/1.flac
-i resources/ffmpeg_working/2.flac
-i resources/ffmpeg_working/3.flac
-i resources/ffmpeg_working/4.flac
-filter_complex '[0:0][1:0][2:0][3:0]concat=n=4:v=0:a=1[out]'
-map '[out]'
resources/ffmpeg_working/done.flac

这给出了以下输出

Stream map ''[out]'' matches no streams.

再一次,如果我在Bash shell中执行相同的ffmpeg命令,它会成功连接文件。因此,似乎单引号参数没有正确处理?

3 个答案:

答案 0 :(得分:5)

引用是一个shell特性,以避免shell扩展,否则将被解释为通配符,变量等。当你使用shell以外的东西时,例如clojure / java进程管理工具,没有这样的东西作为通配符或变量,因此没有引用。

答案 1 :(得分:3)

正如提到的合金,您不应该在resources周围使用单引号。与&比较无:

(require '[clojure.java.shell :as shell])
;=> nil
(shell/sh "ls" "-lah" "'resources'")
;=> {:exit 2, :out "", :err "ls: cannot access 'resources': No such file or directory\n"}
(shell/sh "ls" "-lah" "resources")
;=> {:exit 0, :out "total 8.0K\ndrwxrwxr-x 2 alan alan 4.0K Nov  4  2015 .\ndrwxrwxr-x 8 alan alan 4.0K Jul 14 10:25 ..\n", :err ""}

请注意,您根本没有运行任何外壳;你正在直接谈论unix内核,所以不会发生通配符扩展。例如,以下命令查找名为*(星号)的文件,没有shell将*字符解释为代表“任何匹配文件”的代码:

(shell/sh "ls" "-l" "*")
;=> {:exit 2, :out "", :err "ls: cannot access *: No such file or directory\n"}

如果您希望在传递给Linux内核之前让shell解释您的命令,您可能希望使用shell-cmd函数in the Tupelo library

(require '[tupelo.misc :as tm])
nil
clj.core=> (tm/shell-cmd "ls -l *")
{:exit 0, :out "-rw-rw-r-- 1 alan alan 11218 Nov  4  2015 LICENSE\n-rw-rw-r-- 1 alan alan    16 Jul 12 17:59 my-file.txt\n-rw-rw-r-- 1 alan alan   520 Jan 25 08:29 project.clj\n-rw-rw-r-- 1 alan alan   457 Nov  4  2015 README.md\n-rw-rw-r-- 1 alan alan  4975 Nov  4  2015 tmp.txt\n\ncheckouts:\ntotal 0\nlrwxrwxrwx 1 alan alan 17 Dec  7  2015 tupelo -> /home/alan/tupelo\n\ndoc:\ntotal 4\n-rw-rw-r-- 1 alan alan 101 Nov  4  2015 intro.md\n\nresources:\ntotal 0\n\nsrc:\ntotal 4\ndrwxrwxr-x 2 alan alan 4096 Feb  4 16:28 clj\n\ntarget:\ntotal 4\ndrwxrwxr-x 4 alan alan 4096 Jul 14 10:25 base+system+user+dev\n\ntest:\ntotal 4\ndrwxrwxr-x 3 alan alan 4096 Dec  7  2015 tst\n", :err ""}

请注意,shell-cmd只接受一个直接传递给shell的字符串参数,而不是像clojure.java.shell/sh这样的多个参数。

答案 2 :(得分:0)

如果需要外壳功能,可以直接调用外壳。 Bash支持-c在正常的bash上下文中运行某些命令。当您需要从~/.profile~/.bashrc进行设置时,这很有用。

(require '[clojure.java.shell :as shell])

(defn bash [command]
  (shell/sh "bash" "-c"
            command))

(bash "ls -lah 'resources'")
;; => {:exit 0, :out "lots of text", :err ""}

您现在可以使用任何Shell功能。管道示例:

(bash "find . | grep .clj")
;; => {:exit 0, :out "./src/user.clj\n", :err ""}

您的shell只是可以启动其他应用程序的应用程序。您可以告诉Shell启动应用程序。当您使用clojure.java.shell/sh启动应用程序时,您不是在bash之上执行 ,而是以bash启动其应用程序的方式。

不通过bash间接访问的优势:

  • 您可以更好地控制输入。如果想将输入传递给bash,则很难将其转义。
  • 您有意不依赖Shell中的配置。这些信息通常在系统之间是非常不同的,并且可能在生产中引起问题。