จะรัน Unix shell script ได้อย่างไรจากโค้ด Java?


155

มันค่อนข้างง่ายในการเรียกใช้คำสั่ง Unix จาก Java

Runtime.getRuntime().exec(myCommand);

แต่มันเป็นไปได้ไหมที่จะรันเชลล์สคริปต์ Unix จากโค้ด Java? ถ้าใช่จะเป็นการดีที่จะใช้เชลล์สคริปต์จากภายในโค้ด Java หรือไม่?


3
สิ่งที่น่าสนใจหากเชลล์สคริปต์นั้นเป็นแบบโต้ตอบ
ernesto

ตัวแปร myCommand คือ String อะไร ถ้าใช่แล้วมันจะไม่ทำงานวิธี exec ต้องใช้สตริง [] และอาร์กิวเมนต์ดูด้านล่างตอบของฉันมันทำงานได้อย่างสมบูรณ์แบบ
Girdhar Singh Rathore

คำตอบ:


174

จริงๆคุณควรดูที่สร้างกระบวนการ มันถูกสร้างขึ้นสำหรับสิ่งนี้

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
โปรดทราบว่าคุณอาจต้องระบุโปรแกรม / bin / bash หรือ sh เพื่อดำเนินการสคริปต์ภายในขึ้นอยู่กับการกำหนดค่าของ Java (ดูstackoverflow.com/questions/25647806/… )
ฮอลแลนด์ Holland

@Milhous ฉันรู้ว่ามันค่อนข้างช้าและสิ่งต่าง ๆ อาจเปลี่ยนแปลงไป แต่ตามเอกสาร Java Process ปัจจุบันนี่ไม่ใช่วิธีที่แนะนำสำหรับเชลล์สคริปต์: docs.oracle.com/javase/8/docs/api/java/lang/Process.html " วิธีการที่สร้างกระบวนการอาจทำงานได้ไม่ดีสำหรับกระบวนการพิเศษบนแพลตฟอร์มเนทีฟบางตัวเช่นกระบวนการสร้างหน้าต่างแบบดั้งเดิมกระบวนการ daemon กระบวนการ Win16 / DOS บน Microsoft Windows หรือสคริปต์เชลล์ "
Harman

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

@KisHanSarsecHaGajjar เราสามารถจับเอาท์พุทจาก shellscript และแสดงมันใน java ui ฉันต้องการทราบว่าเป็นไปได้หรือไม่ที่จะทำเช่นนั้น
กระถิน Sama

@KranthiSama คุณสามารถตั้งค่าOutputStreamสำหรับDefaultExecuterการใช้วิธีการในการส่งออกจับในDefaultExecuter.setStreamHandler OutputStreamโปรดอ้างอิงหัวข้อนี้สำหรับข้อมูลเพิ่มเติม: ฉันจะจับเอาท์พุทของคำสั่งได้อย่างไร
ไม่ใช่ข้อผิดพลาด

1
ลิงก์เพื่อเพิ่ม Apache Commons Exec
ไลบรารี่

2
ทางออกที่ดีที่สุด
Dev

23

ฉันจะบอกว่ามันไม่ได้อยู่ในจิตวิญญาณของ Java ที่จะเรียกใช้เชลล์สคริปต์จาก Java Java หมายถึงการเป็น cross platform และการรันเชลล์สคริปต์จะ จำกัด การใช้เพียง UNIX

จากที่กล่าวมาเป็นไปได้แน่นอนที่จะเรียกใช้เชลล์สคริปต์จากภายใน Java คุณจะใช้ไวยากรณ์เดียวกับที่คุณระบุไว้อย่างแน่นอน (ฉันไม่ได้ลองด้วยตัวเอง แต่ลองเรียกใช้งานเชลล์สคริปต์โดยตรงและหากไม่ได้ผลให้สั่งงานเชลล์เองผ่านสคริปต์ในพารามิเตอร์บรรทัดคำสั่ง) .


43
ใช่ แต่ในหลาย ๆ วิธีที่ "จิตวิญญาณของ Java" หรือ "เขียนครั้งเดียวทำงานได้ทุกที่" มนต์เป็นตำนานอยู่แล้ว
BobbyShaftoe

15
เป็นตำนานเกี่ยวกับอะไร
Chris Ballance

15
สิ่งที่เป็นตำนานเกี่ยวกับเรื่องนี้ก็คือคุณมักจะเขียนหนังสือจำนวนมากswitchesและifแถลงการณ์เพื่อแก้ไขความแตกต่างที่ไม่เหมือนกันบนแพลตฟอร์มที่แตกต่างกันแม้จะมีความพยายามอย่างดีที่สุดของคนที่มากับห้องสมุดแกน java
Brian Sweeney

2
น่าแปลกที่พอ! ฉันเห็นด้วยกับความคิดเห็นทั้งหมดข้างต้นและคำตอบ!
AnBisw

11
@ BobShaftoe ฉันเขียน java เป็นเวลา 16 ปีและพัฒนาบน windows เสมอทุกแอปของฉันถูกปรับใช้บน solaris / ibm หรือ oracle ปรุงแต่งกล่องยูนิกซ์ดังนั้นฉันจึงไม่รู้ว่าคุณกำลังพูดถึงอะไร
Kalpesh Soni

21

ฉันคิดว่าคุณได้ตอบคำถามของคุณด้วย

Runtime.getRuntime().exec(myShellScript);

เกี่ยวกับว่าเป็นวิธีปฏิบัติที่ดี ... คุณพยายามทำอะไรกับเชลล์สคริปต์ที่คุณไม่สามารถทำกับ Java ได้?


ฉันประสบกับสถานการณ์ที่คล้ายกันซึ่งฉันจำเป็นต้องซิงค์ไฟล์บางไฟล์บนเซิร์ฟเวอร์ที่แตกต่างกันเมื่อเกิดเงื่อนไขบางอย่างในรหัส java ของฉัน มีวิธีอื่นที่ดีกว่านี้ไหม?
v kumar

1
@Chris Ballance ... ฉันรู้ว่าความคิดเห็นนี้เกือบสิบปีแล้ว :) แต่เพื่อตอบคำถามของคุณจะเกิดอะไรขึ้นถ้าโปรแกรมของฉันต้องโต้ตอบกับช่องทางดาวน์สตรีมและอัปสตรีมครึ่งโหลและขึ้นอยู่กับโหมดการสื่อสารที่เป็นที่ยอมรับ โดยเฉพาะอย่างยิ่งเมื่อคุณกำลังทำงานในโครงการที่ติดต่อกับช่องคี่จำนวนมาก :)
ตะลึงลาน

การผลักดันฟังก์ชันการทำงานให้กับเชลล์สคริปต์นั้นเป็นความพยายามครั้งสุดท้ายหากไม่มีวิธีอื่นในการทำงานใน Java มันจะต้องมีความซับซ้อนในการประสานงานสถานะและการพึ่งพาถ้าคุณผลักดันงานออกมาเป็นเชลล์สคริปต์ ในบางครั้งเชลล์สคริปต์เป็นวิธีเดียวหรือกรอบเวลาทำให้เป็นวิธีเดียวที่สมเหตุสมผลในการทำงานให้สำเร็จดังนั้นนี่คือวิธีทำเช่นนั้น
Chris Ballance

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

เพื่อหลีกเลี่ยงไม่ให้ hardcode พา ธ สัมบูรณ์คุณสามารถใช้วิธีการต่อไปนี้ที่จะค้นหาและรันสคริปต์ของคุณหากอยู่ในไดเรกทอรีรากของคุณ

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

สำหรับฉันทุกสิ่งต้องเรียบง่าย สำหรับการรันสคริปต์ก็ต้องดำเนินการ

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

3

ZT กระบวนการปฏิบัติการห้องสมุดเป็นทางเลือกให้ Apache Commons Exec มันมีฟังก์ชั่นในการเรียกใช้คำสั่งจับผลลัพธ์ของพวกเขาตั้งค่าหมดเวลา ฯลฯ

ฉันยังไม่ได้ใช้ แต่ก็มีเอกสารที่สมเหตุสมผลพอสมควร

ตัวอย่างจากเอกสารประกอบการเรียกใช้งานคำสั่งปั๊ม stderr ไปยังตัวบันทึกส่งคืนผลลัพธ์เป็นสตริง UTF8

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

เอกสารประกอบแสดงถึงข้อดีดังต่อไปนี้ของ Commons Exec:

  • ปรับปรุงการจัดการสตรีม
    • การอ่าน / เขียนไปยังสตรีม
    • การเปลี่ยนเส้นทาง stderr ไปยัง stdout
  • ปรับปรุงการจัดการการหมดเวลา
  • ปรับปรุงการตรวจสอบรหัสออก
  • ปรับปรุง API
    • หนึ่งสมุทรสำหรับกรณีการใช้งานที่ค่อนข้างซับซ้อน
    • หนึ่ง liners เพื่อรับผลลัพธ์ของกระบวนการเป็น String
    • เข้าถึงวัตถุกระบวนการที่มีอยู่
    • รองรับกระบวนการ async ( อนาคต )
  • ปรับปรุงการบันทึกด้วยSLF4J API
  • รองรับหลายกระบวนการ

2

นี่คือตัวอย่างวิธีการรัน Unix bash หรือ Windows bat / cmd script จาก Java อาร์กิวเมนต์สามารถส่งผ่านไปยังสคริปต์และเอาต์พุตที่ได้รับจากสคริปต์ วิธีการรับจำนวนข้อโต้แย้งโดยพลการ

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-like (โดยมี '/' เป็นตัวคั่น) เมื่อใช้งานบน Windows - ใช้ '\' Hier เป็นตัวอย่างของสคริปต์ทุบตี (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 เป็นสคริปต์ Windows cmd แบบง่าย 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) เป็นบรรทัดแรกของสคริปต์และตรวจสอบให้แน่ใจว่ามีการดำเนินการอนุญาตสำหรับไฟล์

ตัวอย่างเช่นหากเป็นสคริปต์ทุบตีให้ใส่ #! / 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 สำหรับนักพัฒนาในอนาคต

  1. ผมทำงานในฤดูใบไม้ผลิ 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. 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"); 

การตรวจสอบระบบปฏิบัติการบนสามารถจัดการเชลล์ / bash scrips ได้หากได้รับการสนับสนุน หากจำเป็นต้องทำรหัสแบบพกพา


0

สำหรับการใช้งานลินุกซ์

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<>());
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.