【问题标题】:Java, Runtime.exec or ProcessBuilder: how to know if the file is shell or binary?Java、Runtime.exec 或 ProcessBuilder:如何知道文件是 shell 还是二进制文件?
【发布时间】:2012-02-05 17:47:17
【问题描述】:

我正在寻找一种最有效的决定方式:

  • 我是否应该在用户提供的命令行中添加 shell 可执行文件
  • 如果是,该可执行文件是什么? (/bin/sh?/usr/bin/perl?/usr/bin/ksh?c:/../cmd.exe?)

众所周知,要从 Java 启动 shell 脚本,应该启动 shell:

ProcessBuilder pb = new ProcessBuilder("/bin/sh", "script.sh", "arg1", "arg2);

要启动二进制文件,应该启动二进制文件本身:

ProcessBuilder pb = new ProcessBuilder("/path/binary", "arg1", "arg2);

如果使用 shell 执行二进制文件,则会产生错误:

ProcessBuilder pb = new ProcessBuilder("/bin/sh", "/path/binary", "arg1", "arg2);
(sh: cannot execute binary file)

如果在没有 shell 二进制文件的情况下执行 shell 脚本,则会产生错误:

ProcessBuilder pb = new ProcessBuilder("script.sh", "arg1", "arg2);
(error 2: file not found)

我的应用程序不知道它从什么开始,二进制或脚本。

启动的应用程序是最终用户提供的事件处理程序。它很可能是在 Unix 下执行的 shell 脚本;但它可以是 Windows 下的 *.cmd,也可以是在某个不起眼的平台下执行的 Perl 脚本。毕竟是 Java。

我第一次天真的尝试是用 shell 启动命令行,看看它是否有效。如果没有,请尝试将其作为二进制文件执行。

这是丑陋和危险的:在某些未知的平台和外壳组合下,第二次运行可能仍然执行脚本,第二次,结果不可预测。

另外,从我无法启动脚本开始,我无法判断脚本何时启动正常并由于自身的某些问题而失败。

我现在考虑的最好的事情是:

  • 阅读脚本并查找任何不可打印的字节
  • 如果找到,将其视为二进制文件
  • 如果没有,添加 /bin/sh(或 cmd.exe,如果在 Windows 下)

如果您有更好的想法,请告知。

更新/部分解决方案

感谢所有与我分享想法的人。

事实证明,我把自己和互联网上的其他人都搞糊涂了 :)

不需要在用户输入的命令行之前添加 shall 二进制文件,前提是:

  1. 脚本在 PATH 中
  2. (对于 Unix)脚本是可执行的
  3. (对于 Unix)脚本有 #!/path/to/interpreter

当我测试我的代码时,其中一个或另一个条件没有满足。 :-(

从头开始仔细执行测试后,脚本已经执行。

第 3 点只能由用户完成,并且必须记录在用户手册中。

由于这些脚本传播到目标系统的方式,它们可能无法执行并且可能不在 PATH 中。

我关心的唯一路径是相对路径,所以在任何相对路径前面加上 ./ 就足够了。

使脚本在 Unix(和任何其他平台)下可执行是一个更大的挑战。这不是WORA。将 /bin/sh 放在它前面可能会有所帮助,但如果我记得在 Solaris 下,shell 将不会执行不可执行的脚本。

我将在本周晚些时候发布另一个更新。

【问题讨论】:

  • 您的 shell 脚本顶部是否有适当的 shebang 行 (#!)?如果是这样,那么您可以简单地要求内核运行它(只要它是+x 可执行文件),而不必担心它可能需要哪个命令解释器。
  • 我不知道。该脚本来自最终用户,不随软件提供。

标签: java shell exec processbuilder


【解决方案1】:

开启

ProcessBuilder pb = new ProcessBuilder("/bin/sh", "/path/binary", "arg1", "arg2); (sh: 无法执行二进制文件)

ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c", "/path/binary", "arg1", "arg2); (sh: 无法执行二进制文件)

一种选择是接受解释器路径作为来自用户的另一个参数(可能来自已知值列表)。

(这可能是一条评论。我无法正确格式化)

【讨论】:

    【解决方案2】:

    您必须跳过这些障碍才能运行命令,这应该是一个危险信号。首先是因为这变得非常复杂,其次是因为 Java 被设计为独立于平台的。当您调查特定于操作系统的 hack 以使内置类正常工作时,您应该退后一步,重新检查您的假设。

    ProcessBuilder pb = new ProcessBuilder("script.sh", "arg1", "arg2);
    (error 2: file not found)
    

    请注意,错误消息是“找不到文件”,而不是“无法执行 shell 脚本”或类似的错误。此错误最可能的原因不是您正在执行脚本,而是找不到该脚本。

    如果脚本在当前目录中,则需要在前面添加./。如果您没有为可执行文件设置显式路径,则该可执行文件必须驻留在您的 $PATH 环境变量中的目录之一中。当前目录. 通常默认包含在$PATH 中。

    ProcessBuilder pb = new ProcessBuilder("./script.sh", "arg1", "arg2);
    

    如果脚本名称是用户提供的值,那么我会对用户提出这个要求——您可以为他们添加./,但 UNIX 程序通常会尽量避免过于有帮助。如果他们忘记输入./,那就是他们的问题!

    【讨论】:

    • 感谢您强制冲洗我的大脑。确实,./script 至少有部分帮助。
    【解决方案3】:

    一种可能的解决方案是生成一个脚本,该脚本将执行脚本/二进制文件从您的程序中包装起来。这样你就知道它总是一个脚本。生成的脚本只是执行内部脚本/二进制文件并返回错误代码(并可能重定向输入/输出)。完成后,您可以简单地删除它。 Java 允许您非常轻松地创建临时文件。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-13
      • 1970-01-01
      • 2012-08-04
      • 1970-01-01
      • 2017-07-21
      • 1970-01-01
      • 2012-05-31
      • 2010-10-28
      相关资源
      最近更新 更多