如何从Java代码运行Unix Shell脚本?


155

从Java运行Unix命令非常简单。

Runtime.getRuntime().exec(myCommand);

但是有可能从Java代码运行Unix shell脚本吗?如果是,从Java代码中运行Shell脚本是一个好习惯吗?


3
如果该shell脚本是交互式的,那么事情将会变得有趣。
ernesto 2014年

该字符串的myCommand变量是什么?如果是,那么它将不起作用,exec方法需要String []和参数,请参见下面的回答,它可以正常工作
Girdhar Singh Rathore

Answers:


174

您应该真正看一下Process Builder。它确实是为这种事情而构建的。

ProcessBuilder pb = new ProcessBuilder("myshellScript.sh", "myArg1", "myArg2");
 Map<String, String> env = pb.environment();
 env.put("VAR1", "myValue");
 env.remove("OTHERVAR");
 env.put("VAR2", env.get("VAR1") + "suffix");
 pb.directory(new File("myDir"));
 Process p = pb.start();

3
从JAVA调用脚本是一种好习惯吗?有性能问题吗?
kautuksahni

1
请注意,您可能需要指定程序/斌/ bash或SH取决于Java的配置中执行脚本(见stackoverflow.com/questions/25647806/...
本·霍兰

@Milhous我知道这已经很晚了,事情可能已经改变了,但是根据当前的Java Process文档,这不是推荐用于Shell脚本的方法:docs.oracle.com/javase/8/docs/api/java/lang/Process.html “创建进程的方法可能不适用于某些本机平台上的特殊进程,例如本机窗口进程,守护进程,Microsoft Windows上的Win16 / DOS进程或Shell脚本。”
哈曼

24

您也可以使用Apache Commons exec库

范例:

package testShellScript;

import java.io.IOException;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteException;

public class TestScript {
    int iExitValue;
    String sCommandString;

    public void runScript(String command){
        sCommandString = command;
        CommandLine oCmdLine = CommandLine.parse(sCommandString);
        DefaultExecutor oDefaultExecutor = new DefaultExecutor();
        oDefaultExecutor.setExitValue(0);
        try {
            iExitValue = oDefaultExecutor.execute(oCmdLine);
        } catch (ExecuteException e) {
            System.err.println("Execution failed.");
            e.printStackTrace();
        } catch (IOException e) {
            System.err.println("permission denied.");
            e.printStackTrace();
        }
    }

    public static void main(String args[]){
        TestScript testScript = new TestScript();
        testScript.runScript("sh /root/Desktop/testScript.sh");
    }
}

为了进一步参考,还在Apache Doc上给出了一个示例。


我可以在Windows中运行它吗?
bekur 2014年

@KisHanSarsecHaGajjar我们还可以捕获shellscript的输出并将其显示在java ui中。我想知道是否可以这样做
Kranthi Sama 2015年

@KranthiSama您可以设置OutputStreamDefaultExecuter使用DefaultExecuter.setStreamHandler方法在捕获输出OutputStream。请参考该线程以获取更多信息:我如何捕获命令的输出...
不是Bug

1
链接以将Apache Commons Exec库依赖项添加到您的项目中-commons.apache.org/proper/commons-exec/dependency-info.html
aunlead

2
最好的解决方案。
开发人员

23

我要说的是,从Java运行Java的Shell脚本不是Java 的精神。Java被认为是跨平台的,运行shell脚本会将其使用范围限制为仅UNIX。

话虽如此,从Java内部运行Shell脚本绝对是可能的。您将使用与列出的语法完全相同的语法(我自己没有尝试过,但是尝试直接执行shell脚本,如果不行,请执行shell本身,将脚本作为命令行参数传入) 。


43
是的,但是在许多方面,“ Java的精神”或“编写一次随处可见”的口头禅还是一个神话。
BobbyShaftoe,2009年

15
这有什么神话?
克里斯·巴伦斯

15
什么是神话它就是你平时写出来无数switchesif语句来解决所有不尽管谁与Java核心类库想出了人民的最大努力工作,在不同平台上完全相同的细微差别。
Brian Sweeney

2
令人惊讶的是!我同意以上所有评论和答案!
AnBisw 2012年

11
@BobbyShaftoe我已经编写Java已有16年了,并且始终在Windows上进行开发,我的所有应用程序始终都部署在solaris / ibm或oracle风格的unix框中,所以我不知道您在说什么
Kalpesh Soni

21

我认为您已经回答了自己的问题

Runtime.getRuntime().exec(myShellScript);

关于这是否是一种好习惯……您打算如何处理Java无法使用的shell脚本?


我遇到过类似的情况,当我的Java代码中发生某种情况时,我需要在不同服务器上重新同步一些文件。还有其他更好的方法吗?
v kumar

1
@Chris Ballance ...我知道这条评论将在10年之后:)但要回答您的问题,如果我的程序必须与六个下游通道和上游通道进行交互,并取决于它们接受的通信方式,该怎么办?尤其是当您正在从事与众多奇数渠道互动的项目时:)
Stunner

如果没有其他方法可以用Java完成这项工作,那么将功能扩展到Shell脚本将是最后的努力。如果要将工作推送到Shell脚本,则协调状态和依赖关系将变得非常复杂。有时,shell脚本是唯一的方法或时间表,使其成为完成一些工作的唯一合理的方法,因此这是一种方法。
克里斯·巴伦斯

11

是的,可以这样做。这为我解决了。

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

import org.omg.CORBA.portable.InputStream;

public static void readBashScript() {
        try {
            Process proc = Runtime.getRuntime().exec("/home/destino/workspace/JavaProject/listing.sh /"); //Whatever you want to execute
            BufferedReader read = new BufferedReader(new InputStreamReader(
                    proc.getInputStream()));
            try {
                proc.waitFor();
            } catch (InterruptedException e) {
                System.out.println(e.getMessage());
            }
            while (read.ready()) {
                System.out.println(read.readLine());
            }
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }

7

这是我的例子。希望有道理。

public static void excuteCommand(String filePath) throws IOException{
    File file = new File(filePath);
    if(!file.isFile()){
        throw new IllegalArgumentException("The file " + filePath + " does not exist");
    }
    if(isLinux()){
        Runtime.getRuntime().exec(new String[] {"/bin/sh", "-c", filePath}, null);
    }else if(isWindows()){
        Runtime.getRuntime().exec("cmd /c start " + filePath);
    }
}
public static boolean isLinux(){
    String os = System.getProperty("os.name");  
    return os.toLowerCase().indexOf("linux") >= 0;
}

public static boolean isWindows(){
    String os = System.getProperty("os.name");
    return os.toLowerCase().indexOf("windows") >= 0;
}

5

是的,有可能,您已经回答了!关于好的实践,我认为最好从文件而不是直接从代码中启动命令。所以,你必须让Java运行在现有的命令(或一个命令)的列表.bat.sh.ksh...文件。这是在文件中执行命令列表的示例MyFile.sh

    String[] cmd = { "sh", "MyFile.sh", "\pathOfTheFile"};
    Runtime.getRuntime().exec(cmd);

4

为了避免必须对绝对路径进行硬编码,可以使用以下方法来查找并执行脚本(如果该脚本位于根目录中)。

public static void runScript() throws IOException, InterruptedException {
    ProcessBuilder processBuilder = new ProcessBuilder("./nameOfScript.sh");
    //Sets the source and destination for subprocess standard I/O to be the same as those of the current Java process.
    processBuilder.inheritIO();
    Process process = processBuilder.start();

    int exitValue = process.waitFor();
    if (exitValue != 0) {
        // check for errors
        new BufferedInputStream(process.getErrorStream());
        throw new RuntimeException("execution of script failed!");
    }
}


3

ZT处理执行库是Apache的百科全书Exec的一种替代方法。它具有运行命令,捕获命令输出,设置超时等功能。

我还没有使用过它,但是它看起来有据可查。

文档中的示例:执行命令,将stderr泵到记录器,将输出作为UTF8字符串返回。

 String output = new ProcessExecutor().command("java", "-version")
    .redirectError(Slf4jStream.of(getClass()).asInfo())
    .readOutput(true).execute()
    .outputUTF8();

它的文档列出了Commons Exec的以下优点:

  • 改进的流处理
    • 读取/写入流
    • 将stderr重定向到stdout
  • 改进了超时处理
  • 改进了对退出代码的检查
  • 改进的API
    • 一个用于非常复杂的用例的衬里
    • 一个衬里将过程输出转换成字符串
    • 访问可用的Process对象
    • 支持异步流程(未来
  • 使用SLF4J API改进了日志记录
  • 支持多个流程

2

这是一个如何从Java运行Unix bash或Windows bat / cmd脚本的示例。可以在脚本上传递参数,并从脚本接收输出。该方法接受任意数量的参数。

public static void runScript(String path, String... args) {
    try {
        String[] cmd = new String[args.length + 1];
        cmd[0] = path;
        int count = 0;
        for (String s : args) {
            cmd[++count] = args[count - 1];
        }
        Process process = Runtime.getRuntime().exec(cmd);
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        try {
            process.waitFor();
        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
        while (bufferedReader.ready()) {
            System.out.println("Received from script: " + bufferedReader.readLine());
        }
    } catch (Exception ex) {
        System.out.println(ex.getMessage());
        System.exit(1);
    }
}

在Unix / Linux上运行时,路径必须类似于Unix(以'/'作为分隔符),而在Windows上运行时,请使用'\'。Hier是一个bash脚本(test.sh)的示例,该脚本接收任意数量的参数并将每个参数加倍:

#!/bin/bash
counter=0
while [ $# -gt 0 ]
do
  echo argument $((counter +=1)): $1
  echo doubling argument $((counter)): $(($1+$1))
  shift
done

打电话时

runScript("path_to_script/test.sh", "1", "2")

在Unix / Linux上,输出为:

Received from script: argument 1: 1
Received from script: doubling argument 1: 2
Received from script: argument 2: 2
Received from script: doubling argument 2: 4

Hier是一个简单的cmd Windows脚本test.cmd,它计算输入参数的数量:

@echo off
set a=0
for %%x in (%*) do Set /A a+=1 
echo %a% arguments received

在Windows上调用脚本时

  runScript("path_to_script\\test.cmd", "1", "2", "3")

输出是

Received from script: 3 arguments received

1

可以像其他程序一样执行它。只要确保您的脚本具有正确的#号即可!(she-bang)行作为脚本的第一行,并确保文件具有执行权限。

例如,如果它是bash脚本,则在脚本的顶部放置#!/ bin / bash,同时还要添加chmod + x。

同样,如果这是一个好习惯,那不是,不是,尤其是对于Java,但是如果这样做可以节省大量时间移植大型脚本,并且您没有得到额外的收入;)节省时间,执行脚本,并将移植到Java放在您的长期任务清单上。


1
  String scriptName = PATH+"/myScript.sh";
  String commands[] = new String[]{scriptName,"myArg1", "myArg2"};

  Runtime rt = Runtime.getRuntime();
  Process process = null;
  try{
      process = rt.exec(commands);
      process.waitFor();
  }catch(Exception e){
      e.printStackTrace();
  }  

1

这是一个较晚的答案。但是,我想到了要为将来的开发人员从Spring-Boot应用程序中执行Shell脚本而付出的努力。

  1. 我在Spring-Boot中工作,但无法从Java应用程序中找到要执行的文件,并且该文件正在throw FileNotFoundFoundException。我必须将文件保留在resources目录中,并且必须设置要在pom.xml启动应用程序时扫描的文件,如下所示。

    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <includes>
                <include>**/*.xml</include>
                <include>**/*.properties</include>
                <include>**/*.sh</include>
            </includes>
        </resource>
    </resources>
  2. 之后,我在执行文件时遇到麻烦,并且文件正在返回error code = 13, Permission Denied。然后我必须通过运行此命令使文件可执行-chmod u+x myShellScript.sh

最后,我可以使用以下代码片段执行文件。

public void runScript() {
    ProcessBuilder pb = new ProcessBuilder("src/main/resources/myFile.sh");
    try {
        Process p;
        p = pb.start();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

希望能解决某人的问题。


0

就像Solaris 5.10一样,它./batchstart.sh有一个窍门,我不知道您的操作系统是否接受它\\. batchstart.sh来代替。此双斜杠可能会有所帮助。


0

我认为

System.getProperty("os.name"); 

如果支持,检查操作系统是否可以管理shell / bash脚本。是否需要使代码可移植。


0

供Linux使用

public static void runShell(String directory, String command, String[] args, Map<String, String> environment)
{
    try
    {
        if(directory.trim().equals(""))
            directory = "/";

        String[] cmd = new String[args.length + 1];
        cmd[0] = command;

        int count = 1;

        for(String s : args)
        {
            cmd[count] = s;
            count++;
        }

        ProcessBuilder pb = new ProcessBuilder(cmd);

        Map<String, String> env = pb.environment();

        for(String s : environment.keySet())
            env.put(s, environment.get(s));

        pb.directory(new File(directory));

        Process process = pb.start();

        BufferedReader inputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        BufferedWriter outputReader = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
        BufferedReader errReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));

        int exitValue = process.waitFor();

        if(exitValue != 0) // has errors
        {
            while(errReader.ready())
            {
                LogClass.log("ErrShell: " + errReader.readLine(), LogClass.LogMode.LogAll);
            }
        }
        else
        {
            while(inputReader.ready())
            {
                LogClass.log("Shell Result : " + inputReader.readLine(), LogClass.LogMode.LogAll);
            }
        }
    }
    catch(Exception e)
    {
        LogClass.log("Err: RunShell, " + e.toString(), LogClass.LogMode.LogAll);
    }
}

public static void runShell(String path, String command, String[] args)
{
    try
    {
        String[] cmd = new String[args.length + 1];

        if(!path.trim().isEmpty())
            cmd[0] = path + "/" + command;
        else
            cmd[0] = command;

        int count = 1;

        for(String s : args)
        {
            cmd[count] = s;
            count++;
        }

        Process process = Runtime.getRuntime().exec(cmd);

        BufferedReader inputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        BufferedWriter outputReader = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
        BufferedReader errReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));

        int exitValue = process.waitFor();

        if(exitValue != 0) // has errors
        {
            while(errReader.ready())
            {
                LogClass.log("ErrShell: " + errReader.readLine(), LogClass.LogMode.LogAll);
            }
        }
        else
        {
            while(inputReader.ready())
            {
                LogClass.log("Shell Result: " + inputReader.readLine(), LogClass.LogMode.LogAll);
            }
        }
    }
    catch(Exception e)
    {
        LogClass.log("Err: RunShell, " + e.toString(), LogClass.LogMode.LogAll);
    }
}

和使用;

ShellAssistance.runShell("", "pg_dump", new String[]{"-U", "aliAdmin", "-f", "/home/Backup.sql", "StoresAssistanceDB"});

要么

ShellAssistance.runShell("", "pg_dump", new String[]{"-U", "aliAdmin", "-f", "/home/Backup.sql", "StoresAssistanceDB"}, new Hashmap<>());
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.