bash -c带有参数的麻烦

时间:2013-11-20 22:01:20

标签: java linux windows bash posix

我有一个用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,然后开始注意到并不是所有东西都在运行。 一些程序继续运行得很好,但其他程序没有。经过一段令人尴尬的工作时间后,我逐渐意识到我对于如何支持工作方式不够了解。

目前,我现在安装的版本全部坏了,我遇到了两个问题;

  1. 要么根本找不到可执行文件,要么;

  2. 可执行文件运行,找不到任何必需的参数 - 我确实通过了。

  3. 这可能分为两类:无论如何,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有启动程序的问题。

1 个答案:

答案 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)的事情包括:

任意代码执行:

  1. file="$(rm -rf /)"
  2. file="foo; rm -rf /"
  3. file="`rm -rf /`"
  4. file="&rm -rf /"
  5. file="\nrm -rf /"
  6. 行为破碎:

    1. file="foo bar"
    2. file="foo'bar"
    3. file="foo # bar"
    4. file="foo -bar"
    5. file="foo\\bar"
    6. 我可以继续下去。


      总而言之,Java的Runtime.exec(String[])确实提供了“可靠,可靠的流程上下文”,即使它不是您认为您想要的。

      如果它执行了一个像OS shell之类的命令,它既不稳固也不可靠。

      我相信您目前正在尝试执行shell命令,因为它似乎是一种简单,强大且灵活的方法。这是由例如同样的错误造成的。 Python的“命令”模块(现在不推荐使用“subprocess”,它可以正确执行)。

      如果你确实需要重复过去的错误来学习,那么你应该使用Runtime.exec(String[]),数组中的前两个字符串为"bash""-c" as在此帖子开头的示例中,或"cmd.exe""/C"用于Windows。

      如果要正确执行,请让用户将命令和参数指定为逻辑上单独的字符串。这概括了该过程,并且仍允许用户根据需要指定bash-csomecommand,但也允许用户正确执行此操作。