当前位置: 首页 > 知识库问答 >
问题:

JavaRuntime.exec()适用于某些命令但不适用于其他命令

隗锐进
2023-03-14

我正在做一个与运行时间有关的练习。exec(),我理解这个运行时。exec不是shell解释器,这就是为什么我执行“bash-c'command'”,但出于某种原因,我可以执行像lsbash-c'ls'这样的命令,但不能执行echo或重定向或多个命令。这些都不起作用:

bash -c 'echo 1234'
bash -c 'ls > abc'
bash -c 'ls;id'
bash -c 'ls -al'

这是我的java代码:

import java.util.*;
import java.io.*;

public class runtime {
    public static void main(String args[]) throws Exception{
        String cmd = args[0];
        System.out.println(cmd);
        Process p = Runtime.getRuntime().exec(cmd);
        OutputStream os = p.getOutputStream();
        InputStream in = p.getInputStream();
        DataInputStream dis = new DataInputStream(in);
        String disr = dis.readLine();
        while ( disr != null ) {
                System.out.println("Out: " + disr);
                disr = dis.readLine();
        }
    }
}

我使用以下语法运行上述命令:java运行时“bash-c‘command’”

这是有效的:

$ java runtime "bash -c 'ls'"
bash -c 'ls'
Out: Main.class
Out: Main.java
Out: runtime.class
Out: runtime.java

我在Ubuntu 20.04和zsh上使用openjdk 11.0.15。有人能告诉我为什么Runtime在这种情况下不起作用吗?谢谢!

共有1个答案

宗波涛
2023-03-14

因为shell解析。

这些都是操作系统所没有的概念:

  • “每个空格将一个参数与另一个参数(以及参数列表中的命令)分隔开”的概念。事实上,一个字符串可以运行任何东西的概念;在操作系统层面,这并不是“运行这个东西”API的样子。当你在命令提示符下输入命令时,你的shell应用程序正在将这些字符串“解释”为命令执行请求

相反,操作系统只是希望您为其提供可执行文件的完整路径以及字符串列表,并为标准输入、标准输出和标准错误选择目标。它不会对任何这些进行处理,只会启动您指定的进程,并逐字传递字符串。

你已经有一半了,因为你已经知道了,例如ls

它变得更加复杂:转动*。txt转换成foo。文本条。txt是bash和朋友的任务,例如,如果您试图运行:ls'*。txt'它不起作用。但在windows上,这不是壳牌的工作;shell只是将其逐字传递给dir,命令的工作就是撤销它。真是一团糟,对吧?执行任务很难!

那么,这里怎么了?两件事:

  • 空间分割不起作用
  • 报价申请未完成

当你写作时:

bash -c 'ls >foo'

在bash shell中,bash必须首先将其拆分为一个命令和一系列参数。Bash的做法如下:

  • 命令:bash
  • arg1:-c
  • arg2:ls

它知道ls

代码中,要求bash运行java,然后让java运行bash。所以,bash首先要做的是:

  • cmd:java

使用相同的逻辑工作。然后您的java应用程序获取整个字符串(即args[0]"bash-c'ls

鉴于您使用的是糟糕的方法,您现在要求java执行此拆分操作。毕竟,如果您只是逐字告诉操作系统,请运行"bash-c'ls

因此,不要使用它。

在这种情况下,java没有意识到这些引号意味着它不应该分裂,所以java告诉操作系统:

请运行:

  • cmd:/bin/bash(java确实进行路径查找;但您应该避免这种情况,不要使用相对路径名,您应该始终完整地写出它们)
  • arg1:-c
  • arg2:'ls
  • arg3:

现在你明白为什么这是完全错误的。

相反,您希望java告诉操作系统:

  • cmd:/bin/bash
  • art1:-c
  • art2:<代码>ls

注意:ls

/bin/bash -c 'ls >foo'

在bash中,是因为您[A]希望bash不要处理ls

运行时。exec不是一个外壳,所以引用的东西?运行时。执行官不知道。

这意味着更一般地说,您的“我将编写一个应用程序,您传递给它的整个参数只是运行”的计划过于简单,除非您为其编写完整的引号和转义分析器,否则永远无法工作。

一个简单的解决方法是获取输入,将其写入磁盘上的shell脚本,在其上设置exec标志,并始终运行您刚刚编写的/bin/bash-c/path/to/script,避免任何试图在java中解析任何内容的需要:让bash来做。

我无法解释的一件奇怪的事情是,将'ls'直接传递给/bin/bash-c,引号保持不变,可以正常工作并运行ls,但'ls*'不能,可能是因为现在bash认为您想要的是明显不存在的可执行文件/bin/ls*(文件名中不能有星号,或者至少,这不是ls可执行文件,也不是ls内置文件的别名)。无论如何,你都想通过ls而不带引号。

我们来试试吧!

import java.util.*;
import java.io.*;

public class runtime {
    public static void main(String args[]) throws Exception{
        ProcessBuilder pb = new ProcessBuilder();
        pb.command("/bin/bash", "-c", "echo 1234");
//        pb.command("/bin/bash", "-c", "'echo 1234'");
        Process p = pb.start();
        OutputStream os = p.getOutputStream();
        InputStream in = p.getInputStream();
        DataInputStream dis = new DataInputStream(in);
        String disr = dis.readLine();
        while ( disr != null ) {
                System.out.println("Out: " + disr);
                disr = dis.readLine();
        }
        int errCode = p.waitFor();
        System.out.println("Process exit code: " + errCode);
    }
}

上面的工作正常。将. Command行替换为注释掉的变体,您会注意到它根本不起作用,并且您会收到错误。在我的mac上,我收到了“127”错误;也许这是bash报告:我找不到您尝试执行的命令。0是您在调用waitFor时要查找的内容:这是“无错误”的代码。

 类似资料:
  • 我正在做一个与Runtime.exec()相关的练习,我明白Runtime.exec不是一个shell解释器,这就是为什么我执行"bash-c'命令'"来代替,但是出于某种原因,我可以执行ls之类的命令,但不能执行回显或重定向或多个命令。这些不起作用: 这是我的java代码: 我使用语法运行上述命令: 这工作原理: 我在Ubuntu 20.04和zsh上使用openjdk 11.0.15。有人能告

  • 当我尝试运行时,某些命令会工作,而其他命令会执行,但会失败或执行不同于我的终端的操作。下面是一个自包含的测试用例,演示了效果: 如果我将命令替换为,那么这个示例非常有用,但是对于其他命令——尤其是那些涉及带有空格的文件名的命令——我会收到错误,即使命令显然正在执行: 同时,复制粘贴到我的外壳: 为什么会有区别?它什么时候起作用,什么时候失败?如何使其适用于所有命令?

  • 在pdf文件中,在按钮后面的代码中,我有 exit命令在Phantom PDF(旧版本,2.2)中运行良好,但在Foxit Reader(8.3,相当新的版本)中运行不好。那里什么也没有发生。 我还检查了一个旧的Foxit阅读器版本3.3,它在那里按预期工作。 我尝试了“安全性”和“信任管理器”设置,但这些设置并没有改善这种情况。 我想在按下按钮时关闭读卡器。

  • 我正在使用来自openweathermap的json API数据来获取有关特定城市的信息。 几天前还好好的,现在由于某种原因,每个标有塞尔维亚国家代码“RS”的城市都不能工作了。http://api.openweathermap.org/data/2.5/weather?q =塞尔维亚贝尔格莱德 如果我使用其他国家的城市,例如这个,它是有效的:http://api.openweathermap.o

  • 问题内容: 嗨,我只是简单地尝试在www.example.com上获取h1标签,该标签显示为“ Example Domain”。该代码适用于http://www.example.com,但不适用于https://www.exmaple.com。我该如何解决这个问题?谢谢 问题答案: PhantomJSDriver不支持(所有)DesiredCapabilities。 你会需要: 记录在这里:htt

  • 所以我使用这种方法写入文件,它在windows上运行完全正常,但在mac上运行时,它会创建文件,但它们是空的。 我知道数据是正确的,因为它打印正确。感谢您的任何帮助,这真的让我绊倒了。