Bagaimana menjalankan skrip Unix shell dari kode Java?

155

Cukup sederhana untuk menjalankan perintah Unix dari Java.

Runtime.getRuntime().exec(myCommand);

Tetapi apakah mungkin untuk menjalankan skrip shell Unix dari kode Java? Jika ya, apakah itu praktik yang baik untuk menjalankan skrip shell dari dalam kode Java?

Lii
sumber
3
Banyak hal menjadi menarik jika skrip shell itu interaktif.
ernesto
apa variabel myCommand adalah String itu? jika ya, maka itu tidak akan berfungsi, metode exec membutuhkan String [] dan argumen, lihat di bawah jawaban saya, itu berfungsi dengan baik
Girdhar Singh Rathore

Jawaban:

174

Anda harus benar-benar melihat Process Builder . Itu benar-benar dibangun untuk hal semacam ini.

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();
Milhous
sumber
3
Apakah praktik yang baik untuk memanggil skrip dari JAVA? Adakah masalah kinerja?
kautuksahni
1
Perhatikan bahwa Anda mungkin perlu menentukan program / bin / bash atau sh untuk mengeksekusi skrip dalam tergantung pada konfigurasi Java (lihat stackoverflow.com/questions/25647806/… )
Ben Holland
@Milhous Saya tahu ini agak terlambat dan mungkin ada yang berubah tetapi per dokumentasi Java Process saat ini, metode ini tidak disarankan untuk skrip shell: docs.oracle.com/javase/8/docs/api/java/lang/Process.html " Metode yang menciptakan proses mungkin tidak berfungsi dengan baik untuk proses khusus pada platform asli tertentu, seperti proses windowing asli, proses daemon, proses Win16 / DOS pada Microsoft Windows, atau skrip shell. "
Harman
24

Anda dapat menggunakan pustaka Apache Commons exec juga.

Contoh:

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

Untuk referensi lebih lanjut, Contoh diberikan pada Apache Doc juga.

Bukan bug
sumber
Bisakah saya menjalankan ini di Windows?
bekur
@KisHanSarsecHaGajjar dapatkah kita juga menangkap output dari shellscript dan menampilkannya di java ui. Saya ingin tahu apakah mungkin untuk melakukannya
Kranthi Sama
@KranthiSama Anda dapat mengatur OutputStreamuntuk DefaultExecutermenggunakan DefaultExecuter.setStreamHandlermetode untuk menangkap keluaran OutputStream. Silakan merujuk utas ini untuk info lebih lanjut: Bagaimana saya bisa menangkap output dari perintah ...
Bukan bug
1
Tautan untuk menambahkan ketergantungan perpustakaan Apache Commons Exec ke proyek Anda - commons.apache.org/proper/commons-exec/dependency-info.html
aunlead
2
solusi terbaik.
Dev
23

Saya akan mengatakan bahwa itu tidak benar semangat Jawa untuk menjalankan skrip shell dari Jawa. Java dimaksudkan untuk lintas platform, dan menjalankan skrip shell akan membatasi penggunaannya hanya untuk UNIX.

Dengan itu, sangat mungkin untuk menjalankan skrip shell dari dalam Java. Anda akan menggunakan sintaks yang persis sama dengan yang Anda daftarkan (saya belum mencobanya sendiri, tetapi coba jalankan skrip shell secara langsung, dan jika itu tidak berhasil, jalankan shell itu sendiri, berikan skrip sebagai parameter baris perintah) .

Jack Leow
sumber
43
Ya, tetapi dalam banyak hal mantra "semangat Jawa" atau "menulis sekali jalan ke mana-mana" adalah mitos.
BobbyShaftoe
15
Apa yang mitos tentang hal itu?
Chris Ballance
15
apa mitos tentang itu, bahwa Anda biasanya akhirnya menulis banyak switchesdan ifpernyataan untuk mengatasi semua nuansa yang tidak bekerja persis sama pada platform yang berbeda meskipun upaya terbaik dari orang-orang yang datang dengan perpustakaan inti java.
Brian Sweeney
2
Cukup mengejutkan! Saya setuju dengan semua komentar di atas dan jawabannya!
AnBisw
11
@ BobbyShaftoe Saya telah menulis java selama 16 tahun, dan selalu dikembangkan di windows, semua aplikasi saya selalu digunakan pada solaris / ibm atau oracle box unix flavoured, jadi saya tidak tahu apa yang Anda bicarakan
Kalpesh Soni
21

Saya pikir Anda telah menjawab pertanyaan Anda sendiri

Runtime.getRuntime().exec(myShellScript);

Seperti apakah itu praktik yang baik ... apa yang Anda coba lakukan dengan skrip shell yang tidak dapat Anda lakukan dengan Java?

Chris Ballance
sumber
Saya telah menghadapi situasi yang sama di mana saya perlu rsync beberapa file di server yang berbeda ketika kondisi tertentu terjadi pada kode java saya. Apakah ada cara lain yang lebih baik?
v kumar
1
@ Chris Ballance ... Saya Tahu komentar ini hampir setelah 10 tahun :) tetapi untuk menjawab pertanyaan Anda, bagaimana jika program saya harus berinteraksi dengan setengah lusin saluran hilir dan hulu dan bergantung pada mode komunikasi yang mereka terima. Terutama ketika Anda sedang mengerjakan proyek yang berinteraksi dengan begitu banyak saluran aneh :)
Stunner
Mendorong fungsionalitas ke skrip shell akan menjadi upaya terakhir jika tidak ada cara lain untuk melakukan pekerjaan di Jawa. Akan rumit untuk mengoordinasikan status dan dependensi jika Anda mendorong pekerjaan ke skrip shell. Kadang-kadang, skrip shell adalah satu-satunya cara atau kerangka waktu menjadikannya satu-satunya cara yang masuk akal untuk menyelesaikan sedikit pekerjaan, jadi ini adalah cara untuk melakukannya.
Chris Ballance
11

Ya itu mungkin untuk dilakukan. Ini berhasil bagi saya.

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());
        }
    }
Desta Haileselassie Hagos
sumber
7

Ini contoh saya. Semoga ini masuk akal.

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;
}
Lionel Yan
sumber
5

Ya, itu mungkin dan Anda telah menjawabnya! Tentang praktik yang baik, saya pikir lebih baik untuk meluncurkan perintah dari file dan tidak langsung dari kode Anda. Jadi, Anda harus membuat Java mengeksekusi daftar perintah (atau satu perintah) dalam yang ada .bat, .sh, .ksh... file. Berikut adalah contoh mengeksekusi daftar perintah dalam file MyFile.sh:

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

Untuk menghindari hardcode jalur absolut, Anda dapat menggunakan metode berikut yang akan menemukan dan mengeksekusi skrip Anda jika ada di direktori root Anda.

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

Bagi saya semua hal harus sederhana. Untuk menjalankan skrip hanya perlu dijalankan

new ProcessBuilder("pathToYourShellScript").start();
Vladimir Bosyi
sumber
3

The Proses ZT Pelaksana perpustakaan adalah sebuah alternatif untuk Apache Commons Exec. Ini memiliki fungsionalitas untuk menjalankan perintah, menangkap outputnya, mengatur batas waktu, dll.

Saya belum menggunakannya, tetapi terlihat cukup terdokumentasi dengan baik.

Contoh dari dokumentasi: Menjalankan perintah, memompa stderr ke logger, mengembalikan output sebagai string UTF8.

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

Dokumentasinya mencantumkan keunggulan berikut di atas Commons Exec:

  • Penanganan aliran yang lebih baik
    • Membaca / menulis ke aliran
    • Mengarahkan stderr ke stdout
  • Penanganan timeout yang lebih baik
  • Peningkatan pemeriksaan kode keluar
  • API yang ditingkatkan
    • Satu liner untuk kasing yang cukup rumit
    • Satu liner untuk mendapatkan output proses menjadi sebuah String
    • Akses ke objek Proses yang tersedia
    • Dukungan untuk proses async ( Masa Depan )
  • Peningkatan logging dengan API SLF4J
  • Dukungan untuk berbagai proses
Lii
sumber
2

Berikut adalah contoh cara menjalankan skrip Unix bash atau Windows bat / cmd dari Java. Argumen dapat disampaikan pada skrip dan output yang diterima dari skrip. Metode ini menerima sejumlah argumen yang berubah-ubah.

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

Saat berjalan di Unix / Linux, lintasan harus seperti Unix (dengan '/' sebagai pemisah), saat dijalankan di Windows - gunakan '\'. Hier adalah contoh skrip bash (test.sh) yang menerima jumlah argumen sembarang dan menggandakan setiap argumen:

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

Saat menelepon

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

pada Unix / Linux, hasilnya adalah:

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 adalah cmd Windows script test.cmd sederhana yang menghitung jumlah argumen input:

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

Saat memanggil skrip di Windows

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

Outputnya adalah

Received from script: 3 arguments received
Andrushenko Alexander
sumber
1

Mungkin saja, jalankan saja seperti program lainnya. Pastikan skrip Anda memiliki # yang tepat! (she-bang) baris sebagai baris pertama skrip, dan pastikan ada hak akses pada file tersebut.

Misalnya, jika skrip bash meletakkan #! / Bin / bash di bagian atas skrip, juga chmod + x.

Juga jika itu adalah praktik yang baik, tidak itu tidak, terutama untuk Jawa, tetapi jika itu menghemat banyak waktu Anda porting skrip besar, dan Anda tidak dibayar ekstra untuk melakukannya;) menghemat waktu Anda, eksekutif skrip, dan letakkan porting ke Jawa pada daftar todo jangka panjang Anda.

Kekoa
sumber
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();
  }  
Girdhar Singh Rathore
sumber
1

Ini jawaban yang terlambat. Namun, saya berpikir untuk menempatkan perjuangan yang harus saya tanggung untuk mendapatkan skrip shell yang akan dieksekusi dari aplikasi Spring-Boot untuk pengembang masa depan.

  1. Saya bekerja di Spring-Boot dan saya tidak dapat menemukan file yang akan dieksekusi dari aplikasi Java saya dan itu melempar FileNotFoundFoundException. Saya harus menyimpan file di resourcesdirektori dan harus mengatur file yang akan dipindai pom.xmlketika aplikasi sedang dijalankan seperti berikut.

    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <includes>
                <include>**/*.xml</include>
                <include>**/*.properties</include>
                <include>**/*.sh</include>
            </includes>
        </resource>
    </resources>
  2. Setelah itu saya kesulitan mengeksekusi file dan itu kembali error code = 13, Permission Denied. Kemudian saya harus membuat file dapat dieksekusi dengan menjalankan perintah ini -chmod u+x myShellScript.sh

Akhirnya, saya bisa menjalankan file menggunakan potongan kode berikut.

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

Semoga itu bisa menyelesaikan masalah seseorang.

Reaz Murshed
sumber
0

Sama seperti Solaris 5.10 berfungsi seperti ini, ./batchstart.shada trik yang saya tidak tahu apakah OS Anda menerimanya sebagai \\. batchstart.shgantinya. Slash ganda ini dapat membantu.

Saul
sumber
0

Saya pikir dengan

System.getProperty("os.name"); 

Memeriksa sistem operasi aktif dapat mengelola skrip shell / bash jika didukung. jika perlu untuk membuat kode portabel.

DayaMoon
sumber
0

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

dan untuk penggunaan;

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

ATAU

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