Как запустить скрипт оболочки Unix из кода Java?

122

Простой запуск команды Unix с Java.

Runtime.getRuntime().exec(myCommand);

Но можно ли запустить оболочку Unix script из кода Java? Если да, было бы хорошей практикой запускать оболочку script из кода Java?

  • 3
    Все становится интересным, если этот сценарий оболочки является интерактивным.
  • 0
    что такое переменная myCommand это та строка? если да, то он не будет работать, метод exec требует String [] и аргумента, смотрите мой ответ ниже, он работает отлично
Теги:

14 ответов

159

Вы действительно должны посмотреть Process Builder. Это действительно построено для такого рода вещей.

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? Есть проблемы с производительностью?
  • 1
    Обратите внимание, что вам может потребоваться указать программу / bin / bash или sh для выполнения скрипта в зависимости от конфигурации Java (см. Stackoverflow.com/questions/25647806/… )
23

Я думаю, что вы ответили на свой вопрос с помощью

Runtime.getRuntime().exec(myShellScript);

Что касается хорошей практики... что вы пытаетесь сделать с оболочкой script, которую вы не можете сделать с Java?

  • 0
    Я столкнулся с подобной ситуацией, когда мне нужно rsync несколько файлов на разных серверах, когда в моем Java-коде возникает определенное условие. Есть ли другой лучший способ?
  • 1
    @Chris Ballance ... Я знаю, что этот комментарий почти через 10 лет :), но чтобы ответить на ваш вопрос, что, если моя программа должна взаимодействовать с полдюжиной нисходящих и восходящих каналов и зависеть от принятого ими способа общения. Особенно, когда вы работаете над проектом, который взаимодействует с таким количеством странных каналов :)
Показать ещё 1 комментарий
22

Я бы сказал, что не в духе Java запускать оболочку script из Java. Java должна быть кросс-платформой, а запуск оболочки script ограничит ее использование только UNIX.

С учетом сказанного, безусловно, можно запустить оболочку script из Java. Вы бы использовали тот же самый синтаксис, который вы указали (я сам не пробовал, но попробуйте выполнить оболочку script напрямую, и если это не сработает, выполните сам оболочку, передав script в качестве параметр командной строки).

  • 41
    Да, но во многих отношениях эта мантра «дух Явы» или «пиши один раз - везде» - это миф.
  • 15
    Что в этом мифического?
Показать ещё 6 комментариев
16

Вы также можете использовать библиотеку exec Apache Commons.

Пример:

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.

  • 0
    Могу ли я запустить это в Windows?
  • 0
    @KisHanSarsecHaGajjar мы можем также захватить вывод из шеллскрипта и отобразить его в интерфейсе Java. Я хочу знать, возможно ли это сделать
Показать ещё 3 комментария
8

Да, это возможно. Это сработало для меня.

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

Чтобы избежать жесткого кодирования абсолютного пути, вы можете использовать следующий метод, который найдет и выполнит ваш script, если он находится в корневом каталоге.

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

Вот мой пример. Надеюсь, что это имеет смысл.

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(this.isLinux()){
        Runtime.getRuntime().exec(new String[] {"/bin/sh", "-c", filePath}, null);
    }else if(this.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;
}
2

Для меня все должно быть простым. Для запуска script просто нужно выполнить

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

Да, это возможно, и вы ответили на это! Что касается хороших практик, я думаю, что лучше запускать команды из файлов, а не непосредственно из вашего кода. Поэтому вы должны заставить Java выполнить список команд (или одну команду) в существующих файлах .bat,.sh,.ksh.... Ниже приведен пример выполнения списка команд в файле "MyFile.sh":

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

Библиотека ZT Process Executor является альтернативой 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
    • Один вкладыш для довольно сложных вариантов использования
    • Один лайнер для вывода процесса в строку
    • Доступ к объекту Процесс
    • Поддержка асинхронных процессов (Будущее)
  • Улучшен ведение журнала с помощью API SLF4J
  • Поддержка нескольких процессов
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

Возможно, просто выполните его как любую другую программу. Просто убедитесь, что ваш script имеет правильный #! (she-bang) в качестве первой строки script и убедитесь, что в файле есть разрешения на выполнение.

Например, если это bash script положить #!/bin/bash в верхней части script, также chmod + x.

Также, если это хорошая практика, нет, это не так, особенно для Java, но если это сэкономит вам много времени на перенос большого script, и вам не придется платить за это дополнительно;) save ваше время, выполните script и поместите перенос на Java в свой многолетний список задач.

0

Я думаю,

System.getProperty("os.name"); 

Проверка операционной системы может управлять сценариями оболочки /bash, если они поддерживаются. если необходимо сделать переносимый код.

0

То же самое, что и Solaris 5.10, работает так: ./batchstart.sh есть трюк, который я не знаю, если ваша ОС примет его вместо \\. batchstart.sh. Эта двойная косая черта может помочь.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню