我有一个用Java编写的应用程序需要调用一些二进制可执行文件的实用程序。它运行在运行Java的任何东西上。它需要调用的不是固定集,而是可以由用户选择。
当我在20世纪90年代末第一次写这篇文章时,在我发现直接调用实用程序是非常有问题之前,有大量的溢出的血液。最短的评论是某些程序需要“上下文” “有些人没有,所以广泛的解决方案更难。
我希望它像Java范例声称的那样在各处运行,虽然不想开始任何形式的战争,但我应该指出它不会。我当然希望Java人员能够为我们解决主要的平台差异,但是他们选择不去并且说这只是平台特定的,而不是他们的问题。 (嘿,如果我对Java的“现代发展”一无所知,那么,哦,1.4,请告诉我!)
对于好奇的,简单地说,我有这样的代码:
if (ClientOS.indexOf("Windows") != -1) { if (ClientOS.equals("Windows 95")) { cmd = "command.com /C "; } else if (ClientOS.equals("Windows 98")) { cmd = "command.com /C "; //cmd = "cmd.exe /C "; } else if (ClientOS.equals("Windows NT")) { cmd = "cmd.exe /C "; } else if (ClientOS.equals("Windows 2000")) { cmd = "cmd.exe /C "; } else if (ClientOS.equals("Windows XP")) { cmd = "cmd.exe /C "; } else { cmd = "cmd.exe /C "; } } else {
所以,多年来我已经使用Bash作为一个称职的中介。我为我的应用程序提供了一个可配置的“shell”变量,它通常在Linux上设置为{{1 }}。在Windows上。为了得到一些帮助,我一直在使用Cygwin,并且"/bin/bash"
的典型值,我取消了上面看到的if-block。在此过程中,我注意到有时候,在某些版本中,"C:/cygwin/bin/bash -c"
会阻碍,而在其他情况下,它是必需的。我不知道为什么;直到最近,无论是添加-c还是将其带走都足够了。
然而,最近我升级了一大堆机器,包括Windows(和cygwin)和Linux,然后开始注意到并不是所有东西都在运行。 一些程序继续运行得很好,但其他程序没有。经过一段令人尴尬的工作时间后,我逐渐意识到我对于如何支持工作方式不够了解。
目前,我现在安装的版本全部坏了,我遇到了两个问题;
要么根本找不到可执行文件,要么;
可执行文件运行,找不到任何必需的参数 - 我确实通过了。
这可能分为两类:无论如何,Java调用程序有什么用?!以及POSIX -c
应该如何工作。因为它很容易做到,所以我对Java稍微降级了一点没有改进,所以我现在更专注于BASH
方面。 要明确的是,从已建立的Bash上下文中调用Bash总是按照“specficication”工作,但是,从Java调用不会!我真正需要做的是弄清楚如何使用总是从Java工作。
每个内置的我都尝试过运行,但是,对于非内置函数,我已经知道你需要提供程序的完整路径。除此之外,我想我知道你有时不得不引用整个命令。 BASH的某些版本似乎使用双引号,其他版本需要单引号。
当我第一次发现问题时,在做任何事情之前,我得到了错误,它给出了可执行文件的完整路径,然后是冒号然后空格,重复两次,然后:
BASH
当它运行可执行文件时,我现在收到程序的抱怨(每个可执行文件都是唯一的),它无法找到它的'参数。在一些尝试通过添加或删除-c或更改引用来解决此问题,我有时会得到:
executable_name:-c:第0行:在寻找匹配的'''时出现意外的EOF executable_name:-c:第1行:语法错误:意外的文件结尾
顺便说一句,我发现this StackOverflow文章讨论了一些事情,但它没有帮助。
我可以告诉你,当我练习可执行文件的这种启动时 - 也就是说,引用可执行文件在单引号中调用并将参数悬挂在后面,可执行文件会运行,但不会得到任何参数。
任何意见都赞赏。
JAVA HEROS的最新消息:
不要像第一个不得不假设我没有使用Java内置函数的评论者一样愚蠢。课程我使用Java内置的运行时库。特定的电话看起来像这样:
进程p = Runtime.getRuntime()。exec(cmd);
在让你的内裤完全扭曲之前,你需要弄清楚Java不能创建一个稳固,可靠的过程上下文,这通常是通过在各种平台上运行程序来实现的。如果不是这样,我不会告诉你Java有启动程序的问题。
答案 0 :(得分:0)
我知道这不是你想要听到的,而且我可能会给我写一些嘲笑的评论,但Java在这一方面基本上是正确的。
您遇到的“意外EOF”问题是因为您运行
Runtime.getRuntime().exec("bash -c " + commandToRun);
而不是
Runtime.getRuntime().exec(new String[] { "bash", "-c", commandToRun });
为了解释,在执行外部进程时,库有两种范例:
不安全,不可预测,已弃用的system(3)
风格。这涉及传入一个由OS命令处理器进行评估的字符串。这有十几个陷阱,为安全漏洞和不正确的行为开辟了道路。
安全,健壮的execve(2)
/ execlp(3)
风格。在此样式中,您传入可执行文件和一系列逻辑上分隔的标志。这很好地映射到OS进程调用,并且默认情况下是安全且可靠的。
Runtime.exec(String)
实际上介于这些之间,是一种类似于Runtime.exec(command.split(" "))
的快捷方式。它减少了安全陷阱的数量,但是当命令包含空格时仍然会导致行为中断,就像您所看到的那样。
将破坏看似合理的伪代码system("touch " + file)
的事情包括:
任意代码执行:
file="$(rm -rf /)"
file="foo; rm -rf /"
file="`rm -rf /`"
file="&rm -rf /"
file="\nrm -rf /"
行为破碎:
file="foo bar"
file="foo'bar"
file="foo # bar"
file="foo -bar"
file="foo\\bar"
我可以继续下去。
总而言之,Java的Runtime.exec(String[])
确实提供了“可靠,可靠的流程上下文”,即使它不是您认为您想要的。
如果它执行了一个像OS shell之类的命令,它既不稳固也不可靠。
我相信您目前正在尝试执行shell命令,因为它似乎是一种简单,强大且灵活的方法。这是由例如同样的错误造成的。 Python的“命令”模块(现在不推荐使用“subprocess”,它可以正确执行)。
如果你确实需要重复过去的错误来学习,那么你应该使用Runtime.exec(String[])
,数组中的前两个字符串为"bash"
和"-c"
as在此帖子开头的示例中,或"cmd.exe"
和"/C"
用于Windows。
如果要正确执行,请让用户将命令和参数指定为逻辑上单独的字符串。这概括了该过程,并且仍允许用户根据需要指定bash
,-c
,somecommand
,但也允许用户正确执行此操作。