我有一个用Java编写的应用程序,需要调用一些二进制可执行文件的实用程序。它可以在运行Java的任何程序上运行。它需要调用的不是固定集,而是可以由用户选择。
当我在1990年代后期第一次写这篇文章时,在我发现直接致电公用事业公司的问题特别严重之前,流血太多了。最简短的评论是,有些程序需要“上下文”,而有些则不需要,因此通用的解决方案更难。
我希望它可以像Java范式要求那样在任何地方运行,并且不希望发动任何形式的战争,我应该指出,事实并非如此。我当然希望Java人士愿意为我们解决主要的平台差异,但是他们选择不这样做,并说这只是平台特定的问题,而不是他们的问题。 (嘿,如果我从1.4开始就对Java的“现代开发”一无所知,请告诉我!)
出于好奇,简短地说,我有如下代码:
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上设置为
"/bin/bash"
之类的东西。在Windows上。为了获得帮助,我一直在使用Cygwin,其中的典型值是"C:/cygwin/bin/bash -c"
,并且删除了上面的if块。在整个过程中,我注意到有时在某些版本中,-c
会妨碍您,而在其他情况下,则是必需的。我不知道为什么;直到最近,添加-c或删除它就足够了。但是,最近我升级了一大堆计算机,包括Windows(和cygwin)和Linux,然后开始注意到并不是所有的东西都可以工作了。某些程序可以继续正常运行,而其他程序则不能。在令人尴尬的几个小时之后,我开始意识到我对应该如何工作并不了解。
目前,在我现在安装的版本中,所有内容都被破坏了,并且我遇到了两个问题。
它根本找不到可执行文件,或者;
可执行文件运行,找不到任何必需的参数-
我确实通过了。
这可能分为两类:Java调用程序怎么了!以及POSIX
BASH
应该如何工作。因为很容易做到,所以我对Java进行了一些降级,没有任何改进,因此目前我更专注于BASH
方面。需要明确的是,从已建立的Bash上下文中调用Bash始终可以“按照规范”进行工作,但是从Java中调用则不行!我真正需要做的是弄清楚如何使其始终从Java运行。我尝试过的每个内置程序都可以运行,但是,对于非内置程序,我了解到您需要提供程序的完整路径。除此之外,我想我知道您有时必须引用整个命令。 BASH的某些版本似乎可以使用双引号,而其他版本则需要单引号。
当我第一次发现问题时,在做任何事情之前,我遇到了错误,它给出了可执行文件的完整路径,后跟一个冒号,然后是空格,重复两次,然后:
"cannot execute binary file"
当它运行可执行文件时,我现在从程序(对于每个可执行文件而言是唯一的)收到抱怨,找不到它的参数。在尝试通过添加或删除-c或更改引号来解决此问题时,有时会得到:
可执行文件名:-c:行0:寻找匹配的`''时出现意外的EOF
可执行文件名:-c:第1行:语法错误:文件意外结束
顺便说一句,我发现this StackOverflow文章讨论了一些问题,但这没有帮助。
我可以告诉您,当我练习启动可执行文件时-即用单引号将要调用的可执行文件引号,而将参数悬而未决,则可执行文件会运行,但不会得到任何参数。
任何输入表示赞赏。
JAVA HEROS的更新:
不要像第一个不得不假设我没有使用Java内置函数的评论者那样傻。当然,我使用Java内置的运行时库。特定的调用如下所示:
进程p = Runtime.getRuntime()。exec(cmd);
在弄乱内裤之前,您需要弄清楚Java不会创建可靠,可靠的过程上下文,而在各种平台上运行程序通常会期望这种上下文。如果事实并非如此,我不会告诉您Java在启动程序时遇到问题。
最佳答案
我知道这不是不想让您听到的,我也可能会给我写一些嘲讽的评论,但是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命令,因为这似乎是一种简单,强大且灵活的方式。这与例如Python的“命令”模块(现在不赞成使用“子进程”,因为它可以正确执行)。
如果确实需要重复过去的错误来学习,则应该使用
Runtime.exec(String[])
,数组中的前两个字符串为"bash"
和"-c"
,如本文开头的示例所示。 ,对于Windows,则为"cmd.exe"
和"/C"
。如果要正确执行操作,请让用户将命令和参数指定为逻辑上分开的字符串。这样可以概括该过程,并且仍然允许用户根据需要指定
bash
,-c
,somecommand
,但也允许他们正确地进行操作。
关于java - bash -c遇到参数麻烦,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20107960/