11

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

  • 我应该在用户提供的命令行中添加 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 将不会执行不可执行的脚本。

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

4

3 回答 3

3

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

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 程序通常会尽量避免过于有帮助。如果他们忘记放./,那就是他们的问题!

于 2012-01-09T05:29:35.423 回答
2

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

于 2012-01-09T05:25:10.147 回答
0

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

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

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

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

于 2012-01-09T05:46:01.540 回答