Как вызвать команду оболочки Linux из Java

93

Я пытаюсь выполнить некоторые команды Linux из Java, используя перенаправление (> &) и каналы (|). Как Java может вызывать команды cshили bash?

Я пробовал использовать это:

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

Но он несовместим с перенаправлениями или каналами.

Нарек
источник
2
catи cshне имеют ничего общего друг с другом.
Bombe
4
я могу понять вопрос для других команд, но для кота: какого черта вы просто не читаете в файле?
Atmocreations
8
Все ошибаются в первый раз - Java exec () не использует оболочку базовой системы для выполнения команды (как указывает kts). Перенаправление и конвейерная обработка являются особенностями реальной оболочки и недоступны из функции exec () Java.
SteveD
stevendick: Большое спасибо, у меня возникли проблемы из-за перенаправления и конвейера!
Нарек,
System.exit (0) не находится внутри условной проверки, завершен ли процесс, поэтому он всегда будет завершаться без вывода ошибок. Никогда не пишите условные выражения без фигурных скобок, чтобы избежать именно такой ошибки.

Ответы:

97

exec не выполняет команду в вашей оболочке

пытаться

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

вместо.

РЕДАКТИРОВАТЬ :: В моей системе нет csh, поэтому вместо этого я использовал bash. Следующее сработало для меня

Process p = Runtime.getRuntime().exec(new String[]{"bash","-c","ls /home/XXX"});
KitsuneYMG
источник
@ Нарек. Прости за это. Я исправил это, удалив лишние \ ". Очевидно, они не нужны. У меня нет csh в моей системе, но он работает с bash.
KitsuneYMG
3
Как уже упоминалось, это должно быть независимым, если у вас есть csh или bash, не так ли?
Нарек
@ Нарек. Должен, но я не знаю, как csh обрабатывает аргументы.
KitsuneYMG
1
Спасибо. Это сработало. На самом деле я использовал «sh» вместо «csh».
Фаршид 02
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

Основываясь на примере @ Tim, чтобы создать автономный метод:

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, а не как часть вашего ответа.
vog
Работает с двумя и четырьмя на OSX / Oracle java8. Кажется, есть проблема с вашей исходной средой Java (о природе которой вы не упоминаете)
TheMadsen 05