Як викликати команду оболонки Linux з Java


93

Я намагаюся виконати деякі команди Linux з Java за допомогою переспрямування (> &) та каналів (|). Як може Java Invoke cshабо bashкоманди?

Я намагався використовувати це:

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

Але це не сумісно з переспрямуваннями або трубами.


2
catі cshне мають нічого спільного між собою.
Bombe

4
я можу зрозуміти питання щодо інших команд, але для кота: чому б ти просто не прочитав у файлі?
Атмосфера

8
Вперше всі помиляються - Java exec () не використовує оболонку базової системи для виконання команди (як зазначає kts). Перенаправлення та конвеєр є особливостями справжньої оболонки і недоступні в Java exec ().
SteveD

stevendick: Щиро дякую, у мене виникали проблеми через перенаправлення та трубопроводи!
Narek

System.exit (0) не перебуває під умовною перевіркою, якщо процес завершено, тому він завжди вийде без помилок. Ніколи не пишіть умовні знаки без фігурних дужок, щоб уникнути саме такого роду помилок.

Відповіді:


96

exec не виконує команду у вашій оболонці

спробуй

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

замість цього.

EDIT :: У моїй системі немає csh, тому я використовував bash. У мене працювало наступне

Process p = Runtime.getRuntime().exec(new String[]{"bash","-c","ls /home/XXX"});

@Narek. Вибач за це. Я виправив це, видаливши зайві. "Очевидно, вони не потрібні. У мене немає csh у моїй системі, але він працює з bash.
KitsuneYMG,

3
Як зазначали інші, це має бути незалежним, чи є у вас csh або bash, чи не так?
Нарек

@Narek. Це повинно, але я не знаю; я не знаю, як csh обробляє аргументи.
KitsuneYMG,

1
Дякую. Це спрацювало. Насправді я використовував "sh" замість "csh".
Фаршид

5
Попередження: це рішення, швидше за все, зіткнеться із типовою проблемою зависання, оскільки ви не читали його потоки виводу та помилок. Наприклад: stackoverflow.com/questions/8595748/java-runtime-exec
Євген Сергєєв

32

Використовуйте ProcessBuilder для відокремлення команд та аргументів замість пробілів. Це має працювати незалежно від використовуваної оболонки:

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

Навіть після java.util. *; належним чином імпортовано, я не можу отримати приклад для запуску.
Steve K

@Stephan Я оновив наведений вище приклад коду, щоб видалити оператори ведення журналу та змінні, які були оголошені поза вставленим кодом, і тепер він повинен компілюватися та запускатися. Не соромтеся спробувати повідомити, як це виходить.
Тім

15

Спираючись на приклад @ Тима, щоб зробити самостійний метод:

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

    }
}

(Приклад тесту - це команда, яка перелічує всі файли в каталозі та його підкаталогах, рекурсивно, у хронологічному порядку .)

До речі, якщо хтось може сказати мені, навіщо мені там чотири та вісім зворотних скісних рисок, замість двох і чотирьох, я можу чомусь навчитися. Відбувається ще один рівень невідхилення від того, що я рахую.

Редагувати: Щойно спробував цей самий код на Linux, і там виявляється, що мені потрібно вдвічі менше зворотних скісних рисок у тестовій команді! (Тобто: очікувана кількість двох і чотирьох.) Тепер це вже не просто дивно, це проблема перенесення.


Вам слід задавати своє запитання як нове запитання StackOverflow, а не як частину відповіді.
вог

Працює з двома та чотирма на OSX / Oracle java8. Здається, є проблема з вашим початковим середовищем Java (про яку ви не згадуєте природу)
TheMadsen
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.