Как запустить скрипт оболочки 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.