在 Java 中运行 Bash 命令

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26830617/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 03:21:55  来源:igfitidea点击:

Running Bash commands in Java

javabashruntime.exec

提问by mickzer

I have the following class. It allows me to execute commands through java.

我有以下课程。它允许我通过java执行命令。

public class ExecuteShellCommand {

public String executeCommand(String command) {

    StringBuffer output = new StringBuffer();

    Process p;
    try {
        p = Runtime.getRuntime().exec(command);
        p.waitFor();
        BufferedReader reader = 
                        new BufferedReader(new InputStreamReader(p.getInputStream()));

        String line = "";           
        while ((line = reader.readLine())!= null) {
            output.append(line + "\n");
        }

    } catch (Exception e) {
        e.printStackTrace();
    }

    return output.toString();

}

}

When I run commands, the result of the previous command isn't saved. For example:

当我运行命令时,不会保存上一个命令的结果。例如:

public static void main(String args[]) {

    ExecuteShellCommand com = new ExecuteShellCommand();
    System.out.println(com.executeCommand("ls"));
    System.out.println(com.executeCommand("cd bin"));
    System.out.println(com.executeCommand("ls"));

}

Gives the output:

给出输出:

bin
src


bin
src

Why doesn't the second 'ls' command show the contents of the 'bin' directory?

为什么第二个“ls”命令不显示“bin”目录的内容?

回答by EJK

Each invocation executes in it's own shell. Thus the "cd" of the 2nd invocation is not seen by the third.

每个调用都在它自己的 shell 中执行。因此,第二次调用的“cd”不会被第三次看到。

See: https://docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html#exec(java.lang.String).

请参阅:https: //docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html#exec(java.lang.String)

This states that the command is run in a separate process. Thus you have spawned 3 processes.

这表明该命令在单独的进程中运行。因此,您产生了 3 个进程。

If you want all 3 in the same process, try this:

如果您希望在同一过程中执行所有 3 个操作,请尝试以下操作:

com.executeCommand("ls; cd bin; ls");

回答by Jigar Joshi

each command you are running has its own bash shell, so once you cd to that directory and for next command you are opening new bash shell

您正在运行的每个命令都有自己的 bash shell,因此一旦您 cd 到该目录并为下一个命令打开新的 bash shell

try changing your command to

尝试将您的命令更改为

ls bin

回答by vatsal

Each command is executed individually. They dont share the context.

每个命令都是单独执行的。他们不共享上下文。

回答by René Link

You start a new process with Runtime.exec(command). Each process has a working directory. This is normally the directory in which the parent process was started, but you can change the directory in which your process is started.

您使用 开始一个新进程Runtime.exec(command)。每个进程都有一个工作目录。这通常是启动父进程的目录,但您可以更改启动进程的目录。

I would recommend to use ProcessBuilder

我建议使用ProcessBuilder

ProcessBuilder pb = new ProcessBuilder("ls");
pb.inheritIO();
pb.directory(new File("bin"));
pb.start();

If you want to run multiple commands in a shell it would be better to create a temporary shell script and run this.

如果您想在一个 shell 中运行多个命令,最好创建一个临时的 shell 脚本并运行它。

public void executeCommands() throws IOException {

    File tempScript = createTempScript();

    try {
        ProcessBuilder pb = new ProcessBuilder("bash", tempScript.toString());
        pb.inheritIO();
        Process process = pb.start();
        process.waitFor();
    } finally {
        tempScript.delete();
    }
}

public File createTempScript() throws IOException {
    File tempScript = File.createTempFile("script", null);

    Writer streamWriter = new OutputStreamWriter(new FileOutputStream(
            tempScript));
    PrintWriter printWriter = new PrintWriter(streamWriter);

    printWriter.println("#!/bin/bash");
    printWriter.println("cd bin");
    printWriter.println("ls");

    printWriter.close();

    return tempScript;
}

Of course you can also use any other script on your system. Generating a script at runtime makes sometimes sense, e.g. if the commands that are executed have to change. But you should first try to create one script that you can call with arguments instead of generating it dynamically at runtime.

当然,您也可以使用系统上的任何其他脚本。在运行时生成脚本有时是有意义的,例如,如果执行的命令必须更改。但是您应该首先尝试创建一个可以使用参数调用的脚本,而不是在运行时动态生成它。

It might also be reasonable to use a template engine like velocity if the script generation is complex.

如果脚本生成很复杂,那么使用像速度这样的模板引擎也可能是合理的。

EDIT

编辑

You should also consider to hide the complexity of the process builder behind a simple interface.

您还应该考虑将流程构建器的复杂性隐藏在一个简单的界面后面。

Separate what you need (the interface) from how it is done (the implementation).

将您需要的(接口)与完成方式(实现)分开。

public interface FileUtils {
    public String[] listFiles(String dirpath);
}

You can then provide implementations that use the process builder or maybe native methods to do the job and you can provide different implementations for different environments like linux or windows.

然后,您可以提供使用流程构建器或本机方法来完成这项工作的实现,并且您可以为不同的环境(如 linux 或 windows)提供不同的实现。

Finally such an interface is also easier to mock in unit tests.

最后,这样的接口也更容易在单元测试中模拟。

回答by jmullee

for future reference: running bash commands after cd , in a subdirectory:

供将来参考:在 cd 之后运行 bash 命令,在子目录中:

import java.io.BufferedReader;
import java.io.InputStreamReader;

/*

$ ( D=somewhere/else ; mkdir -p $D ; cd $D ; touch file1 file2 ; )
$ javac BashCdTest.java && java BashCdTest
 .. stdout: -rw-r--r-- 1 ubuntu ubuntu 0 Dec 28 12:47 file1
 .. stdout: -rw-r--r-- 1 ubuntu ubuntu 0 Dec 28 12:47 file2
 .. stderr: /bin/ls: cannot access isnt_there: No such file or directory
 .. exit code:2

*/
class BashCdTest
    {
    static void execCommand(String[] commandArr)
        {
        String line;
        try
            {
            Process p = Runtime.getRuntime().exec(commandArr);
            BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(p.getInputStream()));
            while ((line = stdoutReader.readLine()) != null) {
                // process procs standard output here
                System.out.println(" .. stdout: "+line);
                }
            BufferedReader stderrReader = new BufferedReader(new InputStreamReader(p.getErrorStream()));
            while ((line = stderrReader.readLine()) != null) {
                // process procs standard error here
                System.err.println(" .. stderr: "+line);
                }
            int retValue = p.waitFor();
            System.out.println(" .. exit code:"+Integer.toString(retValue));
            }
        catch(Exception e)
            { System.err.println(e.toString()); }
        }

    public static void main(String[] args)
        {
        String flist = "file1 file2 isnt_there";
        String outputDir = "./somewhere/else";
        String[] cmd = {
            "/bin/bash", "-c",
            "cd "+outputDir+" && /bin/ls -l "+flist+" && /bin/rm "+flist
            };
        execCommand(cmd);
        }
    }

回答by Steve Zobell

You can form one complex bash command that does everything: "ls; cd bin; ls". To make this work you need to explicitly invoke bash. This approach should give you all the power of the bash command line (quote handling, $ expansion, pipes, etc.).

您可以形成一个复杂的 bash 命令来执行所有操作:“ls; cd bin; ls”。要完成这项工作,您需要显式调用 bash。这种方法应该为您提供 bash 命令行的所有功能(引号处理、$ 扩展、管道等)。

/**
 * Execute a bash command. We can handle complex bash commands including
 * multiple executions (; | && ||), quotes, expansions ($), escapes (\), e.g.:
 *     "cd /abc/def; mv ghi 'older ghi '$(whoami)"
 * @param command
 * @return true if bash got started, but your command may have failed.
 */
public static boolean executeBashCommand(String command) {
    boolean success = false;
    System.out.println("Executing BASH command:\n   " + command);
    Runtime r = Runtime.getRuntime();
    // Use bash -c so we can handle things like multi commands separated by ; and
    // things like quotes, $, |, and \. My tests show that command comes as
    // one argument to bash, so we do not need to quote it to make it one thing.
    // Also, exec may object if it does not have an executable file as the first thing,
    // so having bash here makes it happy provided bash is installed and in path.
    String[] commands = {"bash", "-c", command};
    try {
        Process p = r.exec(commands);

        p.waitFor();
        BufferedReader b = new BufferedReader(new InputStreamReader(p.getInputStream()));
        String line = "";

        while ((line = b.readLine()) != null) {
            System.out.println(line);
        }

        b.close();
        success = true;
    } catch (Exception e) {
        System.err.println("Failed to execute bash with command: " + command);
        e.printStackTrace();
    }
    return success;
}

回答by Naveen hut

You could use the bash command "pmset -g batt" like in the method bellow witch returns the battery percentage

您可以像下面的方法一样使用 bash 命令“pmset -g batt”返回电池百分比

public int getPercentage() {
    Process process = null;
    try {
        process = Runtime.getRuntime().exec("pmset -g batt");
    } catch (IOException e) {
        e.printStackTrace();
    }
    BufferedReader reader = new BufferedReader(new InputStreamReader(
            process.getInputStream()));
    String s = null;
    String y = "";
    while (true) {
        try {
            if (!((s = reader.readLine()) != null)) break;
        } catch (IOException e) {
            e.printStackTrace();
        }
        y += s;
        System.out.println("Script output: " + s);
    }
    return Integer.parseInt(y.substring(y.indexOf(')') + 2, y.indexOf('%')));
}