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

Java运行时。exec()适用于某些命令,但不适用于其他命令[重复]

暴乐邦
2023-03-14

我正在做一个与Runtime.exec()相关的练习,我明白Runtime.exec不是一个shell解释器,这就是为什么我执行"bash-c'命令'"来代替,但是出于某种原因,我可以执行lsbash-c'ls'之类的命令,但不能执行回显或重定向或多个命令。这些不起作用:

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'命令'”

这工作原理:

$ 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应用程序正在将这些字符串“解释”为命令执行请求
  • 计算出bash意味着/bin/bash的概念,即$PATH分辨率。
  • *. txt应该引用以. txt结尾的所有文件的概念。
  • $FOO应替换为环境变量'FOO'的值的概念
  • 分隔2个命令的概念,它应该同时运行这两个命令。
  • 单引号和双引号逃避事物的概念。“逃避事物”意味着事物可以导致解释发生。操作系统什么都不解释,因此没有什么可以逃避的。显然,操作系统不知道'是什么,或者"
  • 那个<代码>

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

你已经完成了一半,因为你已经发现了例如ls

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

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

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

当你写作时:

bash -c 'ls >foo'

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

  • 命令:bash

它知道ls

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

  • cmd:java
  • art1:bash-c'ls

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

考虑到你使用的是糟糕的方法,你现在要求java做这个分裂的事情。毕竟,如果你只是一字不差地告诉操作系统,请运行“bash-c'ls”

因此,不要使用它。

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

请运行:

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

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

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

  • cmd:/bin/bash

注:ls

/bin/bash -c 'ls >foo'

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

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

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

一个简单的方法是接受输入,将其写到磁盘上的外壳脚本中,在其上设置exec标志,并始终运行/bin/bash-c /path/to/script-you-just-wrote,避免任何需要尝试解析任何东西在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);
    }
}

以上方法很好。更换。命令与注释掉的变量一起行,您会注意到它根本不起作用,您会得到一个错误。在我的mac电脑上,我得到了一个“127”错误;也许这是bash的反馈:我找不到您试图执行的命令。0是调用waitFor时要查找的内容:这是“无错误”的代码。

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

  • 我有一个受保护的Web API正在运行,我想访问它。在Azure中,应用注册是: 具有Web API租户管理员权限的SPA Web API没有租户管理员权限,因此请求其id的令牌将返回“应用需要访问资源的权限”错误 我正在尝试获取Web API的承载令牌。问题是它不适用于MSAL,但适用于ADAL。 以下是我的“SPA”的ADAL vanilla JS代码: 如果我在“邮递员”中复制令牌并执行请求