java - bash -c遇到参数麻烦

标签 java linux windows bash posix

我有一个用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-csomecommand,但也允许他们正确地进行操作。

关于java - bash -c遇到参数麻烦,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20107960/

相关文章:

linux - 在本地主机上编辑文件时出现问题

linux - authorized_keys 中的奇怪公钥

windows - QueryWorkingSet 在其结果中包含无效页面

windows - 监控桌面堆内存

java - 使用 SAML 实现 SSO

java - 如何在特定条件下停止 Gradle 或 Maven 构建

java - 如何为JavaFX图表着色系列

linux - 某些进程的 map 文件为空

windows - 我的代码出现语法错误

Java - 对非静态字段列表进行静态引用