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


155

Запустить команду Unix из Java довольно просто.

Runtime.getRuntime().exec(myCommand);

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


3
Вещи становятся интересными, если этот сценарий оболочки является интерактивным.
Эрнесто

что такое переменная myCommand это та строка? если да, то он не будет работать, метод exec требует String [] и аргумента, см. ниже мой ответ, он отлично работает
Girdhar Singh Rathore

Ответы:


174

Вы действительно должны смотреть на 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/… )
Бен Холланд,

@ Milhous Я знаю, что уже довольно поздно и что-то может измениться, но для текущей документации по Java-процессам этот метод не рекомендуется для сценариев оболочки: docs.oracle.com/javase/8/docs/api/java/lang/Process.html " Методы, которые создают процессы, могут не работать для специальных процессов на определенных собственных платформах, таких как собственные процессы управления окнами, процессы-демоны, процессы Win16 / DOS в Microsoft Windows или сценарии оболочки ».
Харман

24

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

Пример :

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 .


Могу ли я запустить это в Windows?
bekur

@KisHanSarsecHaGajjar мы можем также захватить вывод из шеллскрипта и отобразить его в интерфейсе Java. Я хочу знать, возможно ли это сделать
Kranthi Sama

@KranthiSama Вы можете установить OutputStreamдля DefaultExecuterиспользования DefaultExecuter.setStreamHandlerметоды для вывода захвата в OutputStream. Пожалуйста, обратитесь к этой теме для получения дополнительной информации: Как я могу захватить вывод команды ...
Не ошибка

1
Ссылка для добавления зависимости библиотеки Apache Commons Exec в ваш проект - commons.apache.org/proper/commons-exec/dependency-info.html
aunlead

2
лучшее решение
Dev

23

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

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


43
Да, но во многих отношениях эта мантра «дух Явы» или «пиши однажды, беги повсюду» - это миф.
BobbyShaftoe

15
Что в этом мифического?
Крис Балланс

15
что мифический о нем, что вы обычно в конечном итоге писать многочисленные switchesи ifзаявления , чтобы обойти все нюансы , которые не работают точно так же на различных платформах , несмотря на все усилия людей , которые пришли с Java основные библиотеки.
Брайан Суини

2
Довольно удивительно! Я согласен со всеми комментариями выше и ответом!
AnBisw

11
@BobbyShaftoe Я пишу Java уже 16 лет и всегда разрабатывал для Windows, все мои приложения всегда развертываются на Unix-системах со соляриком / IBM или Oracle, поэтому я понятия не имею, о чем вы говорите
Kalpesh Soni

21

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

Runtime.getRuntime().exec(myShellScript);

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


Я столкнулся с подобной ситуацией, когда мне нужно rsync несколько файлов на разных серверах, когда в моем коде Java возникает определенное условие. Есть ли другой способ лучше?
v kumar

1
@Chris Ballance ... Я знаю, что этот комментарий почти через 10 лет :), но чтобы ответить на ваш вопрос, что если моя программа должна взаимодействовать с полдюжиной нисходящих и восходящих каналов и зависеть от принятого ими способа общения. Особенно, когда вы работаете над проектом, который взаимодействует с таким количеством странных каналов :)
Stunner

Передача функциональности в сценарий оболочки будет последней попыткой, если в Java нет другого способа выполнить эту работу. Будет непросто согласовать состояние и зависимости, если вы отправляете работу в сценарий оболочки. Иногда сценарий оболочки является единственным способом, или временные рамки делают его единственным разумным способом выполнить небольшую работу, так что это способ сделать это.
Крис Балланс

11

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

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

7

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

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

5

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

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

4

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

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

3

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

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

3

ZT Процесс Исполнитель является альтернативой Apache Commons Exec. Он имеет функции для запуска команд, захвата их вывода, установки таймаутов и т. Д.

Я еще не использовал это, но это выглядит достаточно хорошо документированным.

Пример из документации: Выполнение команды, перекачка stderr в логгер, возвращение вывода в виде строки UTF8.

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

В его документации перечислены следующие преимущества по сравнению с Commons Exec:

  • Улучшена обработка потоков
    • Чтение / запись в потоки
    • Перенаправление stderr на стандартный вывод
  • Улучшена обработка тайм-аутов
  • Улучшена проверка кодов выхода
  • Улучшенный API
    • Один лайнер для довольно сложных случаев использования
    • Один лайнер, чтобы получить вывод процесса в строку
    • Доступ к объекту Process доступен
    • Поддержка асинхронных процессов ( Будущее )
  • Улучшено ведение журнала с SLF4J API
  • Поддержка нескольких процессов

2

Вот пример того, как запустить скрипт Unix bash или Windows bat / cmd из Java. Аргументы могут быть переданы в сценарий и выведены из сценария. Метод принимает произвольное количество аргументов.

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

При работе в Unix / Linux путь должен быть похож на Unix (с разделителем /) в качестве разделителя, при работе в Windows - использовать \. Hier - это пример скрипта bash (test.sh), который получает произвольное количество аргументов и удваивает каждый аргумент:

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

При звонке

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

в Unix / Linux вывод:

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 - это простой сценарий Windows cmd test.cmd, который считает количество входных аргументов:

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

При вызове скрипта в Windows

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

Выход

Received from script: 3 arguments received

1

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

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

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


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

Это поздний ответ. Тем не менее, я подумал о том, чтобы изо всех сил пытаться заставить сценарий оболочки выполняться из приложения Spring-Boot для будущих разработчиков.

  1. Я работал в Spring-Boot, и я не смог найти файл, который будет выполнен из моего Java-приложения, и он выбрасывал FileNotFoundFoundException. Мне нужно было сохранить файл в resourcesкаталоге и настроить файл для сканирования во pom.xmlвремя запуска приложения, как показано ниже.

    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <includes>
                <include>**/*.xml</include>
                <include>**/*.properties</include>
                <include>**/*.sh</include>
            </includes>
        </resource>
    </resources>
  2. После этого у меня были проблемы с выполнением файла, и он возвращался error code = 13, Permission Denied. Затем я должен был сделать файл исполняемым, выполнив эту команду -chmod u+x myShellScript.sh

Наконец, я мог выполнить файл, используя следующий фрагмент кода.

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

Надеюсь, что это решает чью-то проблему.


0

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


0

Я думаю с

System.getProperty("os.name"); 

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


0

для использования в 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);
    }
}

и для использования;

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

ИЛИ

ShellAssistance.runShell("", "pg_dump", new String[]{"-U", "aliAdmin", "-f", "/home/Backup.sql", "StoresAssistanceDB"}, new Hashmap<>());
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.