Làm thế nào để chạy tập lệnh shell Unix từ mã Java?


155

Nó khá đơn giản để chạy một lệnh Unix từ Java.

Runtime.getRuntime().exec(myCommand);

Nhưng có thể chạy tập lệnh shell Unix từ mã Java không? Nếu có, nó có phải là một cách thực hành tốt để chạy một tập lệnh shell từ bên trong mã Java không?


3
Mọi thứ trở nên thú vị nếu kịch bản shell đó tương tác.
ernesto

Biến myCommand là chuỗi đó là gì? nếu có, thì nó sẽ không hoạt động, phương thức exec yêu cầu String [] và đối số, xem bên dưới câu trả lời của tôi, nó hoạt động hoàn hảo
Girdhar Singh Rathore

Câu trả lời:


174

Bạn thực sự nên nhìn vào Process Builder . Nó thực sự được xây dựng cho loại điều này.

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
Có phải là thực hành tốt để gọi các kịch bản từ JAVA? Bất kỳ vấn đề hiệu suất?
kautuksahni

1
Lưu ý rằng bạn có thể cần chỉ định chương trình / bin / bash hoặc sh để thực thi tập lệnh trong tùy thuộc vào cấu hình của Java (xem stackoverflow.com/questions/25647806/ Lỗi )
Ben Holland

@Milhous Tôi biết điều này khá muộn và mọi thứ có thể đã thay đổi nhưng theo tài liệu Quy trình Java hiện tại thì đây không phải là phương pháp được đề xuất cho các tập lệnh shell: docs.oracle.com/javase/8/docs/api/java/lang/Process.html " Các phương thức tạo quy trình có thể không hoạt động tốt đối với các quy trình đặc biệt trên một số nền tảng nhất định, chẳng hạn như quy trình cửa sổ gốc, quy trình trình nền, quy trình Win16 / DOS trên Microsoft Windows hoặc tập lệnh shell. "
Harman

24

Bạn cũng có thể sử dụng thư viện exec của Apache Commons .

Thí dụ :

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

Để tham khảo thêm, Một ví dụ cũng được đưa ra trên Apache Doc .


Tôi có thể chạy cái này trong Windows không?
bekur

@KisHanSarsecHaGajjar chúng ta cũng có thể chụp đầu ra từ shellscript và hiển thị nó trong java ui. Tôi muốn biết là có thể làm như vậy
Kranthi Sama

@KranthiSama Bạn có thể thiết lập OutputStreamđể DefaultExecutersử dụng DefaultExecuter.setStreamHandlerphương thức để nắm bắt đầu ra OutputStream. Vui lòng tham khảo chủ đề này để biết thêm thông tin: Làm cách nào tôi có thể nắm bắt được đầu ra của lệnh ...
Không phải là lỗi

1
Liên kết để thêm phụ thuộc thư viện Exec của Apache vào dự án của bạn - commons.apache.org/proper/commons-exec/dependency-info.html
aunlead 18/03/2016

2
giải pháp tốt nhất.
Dev

23

Tôi muốn nói rằng nó không phải là tinh thần của Java để chạy một kịch bản shell từ Java. Java có nghĩa là đa nền tảng và việc chạy một kịch bản shell sẽ giới hạn việc sử dụng nó chỉ là UNIX.

Như đã nói, chắc chắn có thể chạy một tập lệnh shell từ bên trong Java. Bạn sẽ sử dụng chính xác cùng một cú pháp mà bạn đã liệt kê (Tôi đã tự mình thử nó, nhưng hãy thử thực thi trực tiếp tập lệnh shell và nếu nó không hoạt động, hãy tự thực thi trình bao, chuyển tập lệnh thành tham số dòng lệnh) .


43
Đúng, nhưng theo nhiều cách, câu thần chú "tinh thần Java" hay "viết một lần chạy khắp mọi nơi" dù sao cũng là một huyền thoại.
BobbyShaftoe

15
Có gì hoang đường về nó?
Chris Ballance

15
Điều huyền thoại về nó là, bạn thường kết thúc việc viết rất nhiều switchesifphát biểu để giải quyết tất cả các sắc thái không hoạt động giống hệt nhau trên các nền tảng khác nhau bất chấp những nỗ lực tốt nhất của những người đã đưa ra các thư viện lõi java.
Brian Sweeney

2
Đủ đáng ngạc nhiên! Tôi đồng ý với tất cả các ý kiến ​​trên và câu trả lời!
AnBisw

11
@BulkShaftoe Tôi đã viết java được 16 năm và luôn phát triển trên windows, tất cả các ứng dụng của tôi luôn được triển khai trên các hộp unix có hương vị solaris / ibm hoặc oracle, vì vậy tôi không biết bạn đang nói về điều gì
Kalpesh Soni

21

Tôi nghĩ rằng bạn đã trả lời câu hỏi của riêng bạn với

Runtime.getRuntime().exec(myShellScript);

Về việc liệu đó có phải là một thực tiễn tốt hay không ... bạn đang cố gắng làm gì với một tập lệnh shell mà bạn không thể làm với Java?


Tôi đã gặp phải một tình huống tương tự khi tôi cần phải đồng bộ một vài tệp trên các máy chủ khác nhau khi một điều kiện nhất định xảy ra trong mã java của tôi. Có cách nào khác tốt hơn không?
v kumar

1
@Chris Ballance ... Tôi biết nhận xét này đã gần 10 năm rồi :) nhưng để trả lời câu hỏi của bạn, điều gì sẽ xảy ra nếu chương trình của tôi phải tương tác với nửa tá kênh hạ lưu và ngược dòng và phụ thuộc vào phương thức liên lạc được chấp nhận của họ. Đặc biệt là khi bạn đang làm việc trong một dự án tương tác với rất nhiều kênh lẻ :)
Stunner

Đẩy chức năng ra một kịch bản shell sẽ là nỗ lực cuối cùng nếu không có cách nào khác để thực hiện công việc trong Java. Sẽ rất phức tạp khi phối hợp trạng thái và phụ thuộc nếu bạn đẩy công việc ra một tập lệnh shell. Đôi khi, một kịch bản shell là cách duy nhất hoặc khung thời gian làm cho nó trở thành cách hợp lý duy nhất để hoàn thành một chút công việc, vì vậy đây là một cách để làm điều đó.
Chris Ballance

11

Có nó có thể làm như vậy. Điều này làm việc cho tôi.

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

Đây là ví dụ của tôi. Hy vọng nó có ý nghĩa.

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

Vâng, nó là có thể và bạn đã trả lời nó! Về thực hành tốt, tôi nghĩ tốt hơn là khởi chạy các lệnh từ tệp chứ không phải trực tiếp từ mã của bạn. Vì vậy, bạn cần phải thực hiện Java thực hiện danh sách các lệnh (hoặc một lệnh) trong hiện một .bat, .sh, .kshfile .... Dưới đây là một ví dụ về thực thi danh sách các lệnh trong một tệp MyFile.sh:

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

4

Để tránh phải mã hóa một đường dẫn tuyệt đối, bạn có thể sử dụng phương pháp sau đây sẽ tìm và thực thi tập lệnh của mình nếu nó nằm trong thư mục gốc của bạn.

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

Đối với tôi tất cả mọi thứ phải đơn giản. Để chạy script chỉ cần thực thi

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

3

Các ZT Process Executor thư viện là một thay thế cho Apache Commons Exec. Nó có chức năng chạy các lệnh, nắm bắt đầu ra của chúng, thiết lập thời gian chờ, v.v.

Tôi chưa sử dụng nó, nhưng nó có vẻ được tài liệu hợp lý.

Một ví dụ từ tài liệu: Thực hiện một lệnh, bơm stderr vào một logger, trả về đầu ra dưới dạng chuỗi UTF8.

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

Tài liệu của nó liệt kê các ưu điểm sau so với Commons Exec:

  • Cải thiện xử lý luồng
    • Đọc / viết vào luồng
    • Chuyển hướng stderr sang thiết bị xuất chuẩn
  • Cải thiện xử lý thời gian chờ
  • Cải thiện kiểm tra mã thoát
  • API cải tiến
    • Một lớp lót cho các trường hợp sử dụng khá phức tạp
    • Một lớp lót để có được đầu ra quá trình thành Chuỗi
    • Truy cập vào đối tượng Process có sẵn
    • Hỗ trợ cho các quy trình không đồng bộ ( Tương lai )
  • Cải thiện đăng nhập với API SLF4J
  • Hỗ trợ cho nhiều quy trình

2

Dưới đây là một ví dụ về cách chạy tập lệnh bash Unix hoặc Windows bat / cmd từ Java. Các đối số có thể được truyền vào tập lệnh và đầu ra nhận được từ tập lệnh. Phương thức chấp nhận số lượng đối số tùy ý.

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

Khi chạy trên Unix / Linux, đường dẫn phải giống như Unix (với '/' là dấu phân cách), khi chạy trên Windows - sử dụng '\'. Hier là một ví dụ về tập lệnh bash (test.sh) nhận số lượng đối số tùy ý và nhân đôi mọi đối số:

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

Khi gọi

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

trên Unix / Linux, đầu ra là:

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 là một tập lệnh Windows cmd đơn giản test.cmd đếm số lượng đối số đầu vào:

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

Khi gọi tập lệnh trên Windows

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

Đầu ra là

Received from script: 3 arguments received

1

Có thể, chỉ cần thực hiện nó như bất kỳ chương trình khác. Chỉ cần chắc chắn rằng tập lệnh của bạn có # đúng! (she-bang) là dòng đầu tiên của tập lệnh và đảm bảo có các quyền thực thi trên tệp.

Ví dụ: nếu đó là tập lệnh bash, hãy đặt #! / Bin / bash ở đầu tập lệnh, cũng là chmod + x.

Ngoài ra, nếu nó là một cách thực hành tốt, thì không, đặc biệt là đối với Java, nhưng nếu nó giúp bạn tiết kiệm rất nhiều thời gian để chuyển một tập lệnh lớn và bạn sẽ không được trả thêm tiền để làm điều đó;) hãy tiết kiệm thời gian của bạn, thực hiện tập lệnh và đưa tính năng chuyển sang Java vào danh sách việc cần làm dài hạn của bạn.


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

Đây là một câu trả lời muộn. Tuy nhiên, tôi đã nghĩ đến việc đặt cuộc đấu tranh mà tôi phải chịu để có được một kịch bản shell được thực thi từ ứng dụng Spring-Boot cho các nhà phát triển trong tương lai.

  1. Tôi đã làm việc trong Spring-Boot và tôi không thể tìm thấy tệp được thực thi từ ứng dụng Java của mình và nó đã bị ném FileNotFoundFoundException. Tôi phải giữ tập tin trong resourcesthư mục và phải đặt tập tin được quét trong pom.xmlkhi ứng dụng đang được khởi động như sau.

    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <includes>
                <include>**/*.xml</include>
                <include>**/*.properties</include>
                <include>**/*.sh</include>
            </includes>
        </resource>
    </resources>
  2. Sau đó tôi gặp khó khăn khi thực hiện các tập tin và nó đã trở lại error code = 13, Permission Denied. Sau đó, tôi phải làm cho tập tin thực thi bằng cách chạy lệnh này -chmod u+x myShellScript.sh

Cuối cùng, tôi có thể thực thi tệp bằng đoạn mã sau.

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

Hy vọng rằng giải quyết vấn đề của ai đó.


0

Cũng giống như Solaris 5.10, nó hoạt động như thế này, ./batchstart.shcó một mẹo tôi không biết nếu hệ điều hành của bạn chấp nhận sử dụng \\. batchstart.shthay thế. Dấu gạch chéo đôi này có thể giúp đỡ.


0

Tôi nghĩ với

System.getProperty("os.name"); 

Kiểm tra hệ điều hành trên có thể quản lý các đoạn mã shell / bash nếu được hỗ trợ. nếu có nhu cầu làm cho mã di động.


0

để sử dụng 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);
    }
}

và để sử dụng;

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

HOẶC LÀ

ShellAssistance.runShell("", "pg_dump", new String[]{"-U", "aliAdmin", "-f", "/home/Backup.sql", "StoresAssistanceDB"}, new Hashmap<>());
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.