如何从 Java 代码运行 Unixshell 脚本?

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

Runtime.getRuntime().exec(myCommand);

但是有没有可能从 Java 代码运行 Unix shell 脚本呢?如果是的话,从 Java 代码中运行 shell 脚本是一个好的实践吗?

460132 次浏览

我想你已经回答了你自己的问题

Runtime.getRuntime().exec(myShellScript);

至于这是否是一个好的实践... ... 你试图用一个你不能用 Java 来做的 shell 脚本来做什么?

我想说的是,从 Java 运行 shell 脚本不是 本着爪哇精神。Java 应该是跨平台的,运行 shell 脚本只能在 UNIX 中使用。

也就是说,在 Java 中运行 shell 脚本是完全有可能的。您将使用与您列出的语法完全相同的语法(我自己还没有尝试过,但是尝试直接执行 shell 脚本,如果不行,就执行 shell 本身,将脚本作为命令行参数传入)。

这是可能的,只是执行它作为任何其他程序。只要确保你的剧本是正确的 # !(she-bang)行作为脚本的第一行,并确保对文件有执行权限。

例如,如果它是一个 bash 脚本 put # !/bin/bash 在脚本的顶部,也是 chmod + x。

同样,如果这是一个好的实践,不,它不是,特别是对于 Java,但是如果它节省了你很多时间移植一个大的脚本,你不会得到额外的报酬做它;)节省你的时间,执行脚本,并把移植到 Java 放在你的长期待办事项列表。

你真的应该看看 流程生成器。它真的是为这种事情而建的。

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();

就像 Solaris 5.10一样,它的工作原理就像这个 ./batchstart.sh有一个技巧,我不知道你的操作系统是否接受它使用 \\. batchstart.sh代替。这个双斜线可能会有帮助。

对我来说,一切都必须简单。 对于运行脚本,只需要执行

new ProcessBuilder("pathToYourShellScript").start();

是的,这是有可能的。这对我来说奏效了。

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());
}
}

我觉得

System.getProperty("os.name");

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

也可以使用 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");
}
}

作为进一步的参考,在 阿帕奇博士上也给出了一个例子。

  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();
}

这是我的例子,希望能说得通。

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;
}

ZT 进程执行器库是 ApacheCommons Exec 的替代品。它具有运行命令、捕获命令输出、设置超时等功能。

我还没有使用它,但它看起来相当良好的文档。

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

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

其文件列出了相对于 Commons Exec 的以下优势:

  • 改进流的处理
    • 对流进行读/写操作
    • 将 stderr 重定向到 stdout
  • 改进超时处理
  • 改进了对出口代码的检查
  • 改进的 API
    • 一个非常复杂的用例
    • 将进程输出转换为 String 的一行代码
    • 可以访问 过程对象
    • 支持异步进程(未来)
  • 使用 SLF4J 空气污染指数改进日志记录
  • 支持多进程

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

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

为了避免硬编码一个绝对路径,您可以使用以下方法来查找并执行您的脚本(如果脚本在根目录中的话)。

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!");
}
}

这是一个迟到的回答。然而,我想到了我不得不忍受的挣扎,为未来的开发人员从 Spring-Boot 应用程序中获得一个可以执行的 shell 脚本。

  1. 当时我在 Spring-Boot 工作,我无法从我的 Java 应用程序中找到要执行的文件,它抛出了 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. After that I was having trouble executing the file and it was returning error code = 13, Permission Denied. Then I had to make the file executable by running this command - chmod u+x myShellScript.sh

Finally, I could execute the file using the following code snippet.

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

希望这能解决某人的问题。

下面是一个如何从 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

用于 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<>());