Cara menjalankan perintah shell Linux dari Java

93

Saya mencoba menjalankan beberapa perintah Linux dari Java menggunakan redirection (> &) dan pipe (|). Bagaimana Java dapat memanggil cshatau bashmemerintahkan?

Saya mencoba menggunakan ini:

Process p = Runtime.getRuntime().exec("shell command");

Tapi itu tidak kompatibel dengan pengalihan atau pipa.

Narek
sumber
2
catdan cshtidak ada hubungannya dengan satu sama lain.
Bombe
4
saya dapat memahami pertanyaan untuk perintah lain, tetapi untuk kucing: mengapa Anda tidak membaca di file saja?
Atmocreations
8
Semua orang mendapatkan kesalahan ini pertama kali - exec () Java tidak menggunakan shell sistem yang mendasarinya untuk menjalankan perintah (seperti yang ditunjukkan kts). Redirection dan piping adalah fitur shell nyata dan tidak tersedia dari exec () Java.
SteveD
stevendick: Terima kasih banyak, saya mendapatkan masalah karena pengalihan dan pemipaan!
Narek
System.exit (0) tidak berada di dalam pemeriksaan bersyarat jika proses selesai, sehingga akan selalu keluar tanpa kesalahan keluaran. Jangan pernah menulis persyaratan tanpa tanda kurung, untuk menghindari kesalahan semacam ini.

Jawaban:

97

exec tidak menjalankan perintah di shell Anda

mencoba

Process p = Runtime.getRuntime().exec(new String[]{"csh","-c","cat /home/narek/pk.txt"});

sebagai gantinya.

EDIT :: Saya tidak memiliki csh di sistem saya, jadi saya menggunakan bash sebagai gantinya. Berikut ini berhasil untuk saya

Process p = Runtime.getRuntime().exec(new String[]{"bash","-c","ls /home/XXX"});
KitsuneYMG
sumber
@Tokopedia Maaf soal itu. Saya memperbaikinya dengan menghapus ekstra \ "Tampaknya mereka tidak diperlukan. Saya tidak memiliki csh di sistem saya, tetapi berfungsi dengan bash.
KitsuneYMG
3
Seperti yang disebutkan orang lain, ini harus independen apakah Anda memiliki csh atau bash, bukan?
Narek
@Tokopedia Seharusnya, tapi saya tidak tahu bagaimana csh menangani argumen.
KitsuneYMG
1
Terima kasih. Ini berhasil. Sebenarnya saya menggunakan "sh", bukan "csh".
Farshid
5
Peringatan: solusi ini kemungkinan besar akan mengalami masalah tipikal hang karena Anda tidak membaca output dan aliran kesalahannya. Misalnya: stackoverflow.com/questions/8595748/java-runtime-exec
Evgeni Sergeev
32

Gunakan ProcessBuilder untuk memisahkan perintah dan argumen, bukan spasi. Ini harus berfungsi terlepas dari shell yang digunakan:

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class Test {

    public static void main(final String[] args) throws IOException, InterruptedException {
        //Build command 
        List<String> commands = new ArrayList<String>();
        commands.add("/bin/cat");
        //Add arguments
        commands.add("/home/narek/pk.txt");
        System.out.println(commands);

        //Run macro on target
        ProcessBuilder pb = new ProcessBuilder(commands);
        pb.directory(new File("/home/narek"));
        pb.redirectErrorStream(true);
        Process process = pb.start();

        //Read output
        StringBuilder out = new StringBuilder();
        BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line = null, previous = null;
        while ((line = br.readLine()) != null)
            if (!line.equals(previous)) {
                previous = line;
                out.append(line).append('\n');
                System.out.println(line);
            }

        //Check result
        if (process.waitFor() == 0) {
            System.out.println("Success!");
            System.exit(0);
        }

        //Abnormal termination: Log command parameters and output and throw ExecutionException
        System.err.println(commands);
        System.err.println(out.toString());
        System.exit(1);
    }
}
Tim
sumber
Bahkan setelah java.util. *; diimpor dengan benar. Saya tidak bisa menjalankan contoh di atas.
Steve K
@ Stephan Saya telah memperbarui kode contoh di atas untuk menghapus pernyataan logging dan variabel yang dideklarasikan di luar kode yang ditempelkan, dan sekarang harus dikompilasi & dijalankan. Jangan ragu untuk mencoba dan beri tahu saya cara kerjanya.
Tim
15

Membangun contoh @ Tim untuk membuat metode mandiri:

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.ArrayList;

public class Shell {

    /** Returns null if it failed for some reason.
     */
    public static ArrayList<String> command(final String cmdline,
    final String directory) {
        try {
            Process process = 
                new ProcessBuilder(new String[] {"bash", "-c", cmdline})
                    .redirectErrorStream(true)
                    .directory(new File(directory))
                    .start();

            ArrayList<String> output = new ArrayList<String>();
            BufferedReader br = new BufferedReader(
                new InputStreamReader(process.getInputStream()));
            String line = null;
            while ( (line = br.readLine()) != null )
                output.add(line);

            //There should really be a timeout here.
            if (0 != process.waitFor())
                return null;

            return output;

        } catch (Exception e) {
            //Warning: doing this is no good in high quality applications.
            //Instead, present appropriate error messages to the user.
            //But it's perfectly fine for prototyping.

            return null;
        }
    }

    public static void main(String[] args) {
        test("which bash");

        test("find . -type f -printf '%T@\\\\t%p\\\\n' "
            + "| sort -n | cut -f 2- | "
            + "sed -e 's/ /\\\\\\\\ /g' | xargs ls -halt");

    }

    static void test(String cmdline) {
        ArrayList<String> output = command(cmdline, ".");
        if (null == output)
            System.out.println("\n\n\t\tCOMMAND FAILED: " + cmdline);
        else
            for (String line : output)
                System.out.println(line);

    }
}

(Contoh uji adalah perintah yang mencantumkan semua file dalam direktori dan subdirektorinya, secara rekursif, dalam urutan kronologis .)

Ngomong-ngomong, jika seseorang bisa memberi tahu saya mengapa saya membutuhkan empat dan delapan garis miring terbalik di sana, alih-alih dua dan empat, saya bisa belajar sesuatu. Ada satu tingkat kejadian tidak terhindarkan yang terjadi daripada yang saya hitung.

Sunting: Baru saja mencoba kode yang sama ini di Linux, dan ternyata saya membutuhkan setengah dari garis miring terbalik di perintah tes! (Yaitu: jumlah yang diharapkan dari dua dan empat.) Sekarang bukan lagi hanya aneh, ini masalah portabilitas.

Evgeni Sergeev
sumber
Anda harus mengajukan pertanyaan Anda sebagai pertanyaan StackOverflow baru, bukan sebagai bagian dari jawaban Anda.
vog
Bekerja dengan dua dan empat di OSX / Oracle java8. Sepertinya ada masalah dengan lingkungan java asli Anda (yang tidak Anda sebutkan sifat dasarnya)
TheMadsen