有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

从Java调用“内置”Linux命令

这篇文章是我的一篇文章的后续文章。正如在那篇文章中提到的,某些Linux“命令”是内置的,在使用Runtime.getRuntime().exec(...)方法调用时给出java.io.IOException

history是迄今为止我能找到的唯一一个关于这个问题的命令,但我没有理由相信它是唯一的命令


问题:有没有一种方法可以从Java调用“内置”Linux命令,比如history


共 (3) 个答案

  1. # 1 楼答案

    我不建议这样做,但您可以通过STD输入传递内置命令,直接从Java ProcessBuilder运行shell内置命令

    这是实现history的一种非常不整洁的方式

    private static void run(String[] cmd, byte[] stdin) throws IOException, InterruptedException  {
        ProcessBuilder pb = new ProcessBuilder(cmd);
    
        // No STDERR => merge to STDOUT
        pb.redirectErrorStream(true);
    
        // Launch and wait:
        Process p = pb.start();
    
        // Send + close STDIN stream
        try(OutputStream os = p.getOutputStream()) {
            if (stdin != null) os.write(stdin);
        }
        // Print STDOUT+ERR stream
        try(var stdo = p.getInputStream()) {
            stdo.transferTo(System.out);
        }
    
        int rc = p.waitFor();
        System.out.println("END "+Arrays.toString(cmd)+" rc="+rc);
    }
    

    如果以这种方式对内容进行任何有意义的操作,则需要过滤掉bash reply/prompt:

    run(new String[] { "bash", "-i"}, "history".getBytes());
    

    或者将历史记录保存到文件中,然后在没有shell提示的情况下读取命令输出:

    run(new String[] { "bash", "-i"}, "history > history.txt".getBytes());
    System.out.println("HISTORY:");
    System.out.println(Files.readString(Path.of("history.txt")));
    

    您也可以尝试直接从历史文件中读取

  2. # 2 楼答案

    Certain Linux "commands" are built-in

    这是不正确的。没有这种事

    你大概是在说内置的外壳。有一百万个linux变体和许多shell变体。每个外壳都可以决定自己的内置组件。通常情况下,东西是内置的,但也作为实际的可执行文件存在。例如,我打赌你的linux系统/bin/ls是存在的,但是ls也是大多数shell中的内置程序。试着运行man builtin,它应该列出内置的,可能(所有这些都是用'maybe'和'should'和'maybly'装饰的——linux不是一个单一的想法,有很多发行版,有很多打包的方法。没有linux规范要求man builtin工作)

    当然,你可以运行内置的。但是,/bin/bash有这些内置代码,而不是“linux”。例如,您可以尝试这段代码,它极有可能处理几乎所有可以想象的posix风格,并且应该按照您的实际意图执行,并且不应该有重大的安全问题

    要记住几件事:

    • 永远不要在运行时使用相对路径。执行官。只是不要——这不太可能奏效,你依赖于JAVA解码$PATH(当你输入,比如,curl时,它会运行/usr/bin/curl,因为/usr/bin正在运行中?是的,那是bash,不是linux。linux不知道PATH是什么

    • 始终使用ProcessBuilder,并使用String...List<String>版本,您不想依赖java空间拆分(空间拆分?是的,不是linux)

    • 请记住,星型扩展也是一种shell ism,除了在windows上不是。/usr/bin/imgconvert *.jpg不适用于process builder,因为解包是bash做的事情,java的process builder不会为您做这件事。因此,只传递最明显最简单的论点。不要传递通配符,不要传递任何形式的变量替换

    • 任何时候,除非依赖shell(即,你想运行内置程序或需要shell扩展工具才能工作),否则你都无法完成这项工作,运行shell,并要求它运行命令

    Path p = Paths.get("/bin/ls");
    List<String> cmd;
    if (Files.isExecutable(p)) {
        cmd = List.of("/bin/ls");
    } else {
        // run bash, and tell it to run something immediately instead
        // of running an interactive prompt.
        cmd = List/of("/bin/bash", "-c", "ls");
    }
    
    ProcessBuilder pb = new ProcessBuilder(cmd);
    pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
    pb.start();
    

    如果它存在,它将运行^{(许多内置的do也会显示为可执行文件,即使它们在大多数shell中是内置的),否则,bash。不要试图运行/bin/sh——虽然通常都是这样,但您不知道它是哪个shell,而且编写“linux shell命令ese”的方式非常困难,因为它可以在所有shell中运行。这是可以做到的,但是,哦。需要特殊技能和大量测试。 /bin/bash几乎无处不在,请使用它。即使像炮弹一样猛击也有点恶心

  3. # 3 楼答案

    历史是Bash中的一个命令,所以不是直接的。您需要调用一个shell脚本并使其具有交互性,如

    本地主机:~/tmp>;猫g.sh

    #!/bin/bash
    history
    

    bash-li g.sh