Como executar o script de shell Unix a partir do código Java?


155

É bastante simples executar um comando Unix a partir de Java.

Runtime.getRuntime().exec(myCommand);

Mas é possível executar um script de shell Unix a partir do código Java? Se sim, seria uma boa prática executar um script de shell no código Java?


3
As coisas ficam interessantes se esse script de shell for interativo.
Ernesto

qual é a variável myCommand é essa String? Se sim, então não vai funcionar, o método exec requer String [] e argumento, veja abaixo do meu answar, ele funciona perfeitamente
Girdhar Singh Rathore

Respostas:


174

Você realmente deve olhar para o Process Builder . É realmente construído para esse tipo de coisa.

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
É uma boa prática chamar scripts de JAVA? Algum problema de desempenho?
kautuksahni 18/09/17

1
Observe que você pode precisar especificar o programa / bin / bash ou sh para executar o script, dependendo da configuração do Java (consulte stackoverflow.com/questions/25647806/… )
Ben Holland

@ Milhous Eu sei que isso é tarde demais e as coisas podem ter mudado, mas, segundo a documentação atual do processo Java, este não é o método recomendado para scripts de shell: docs.oracle.com/javase/8/docs/api/java/lang/Process.html " Os métodos que criam processos podem não funcionar bem em processos especiais em determinadas plataformas nativas, como processos de janelas nativas, processos daemon, processos Win16 / DOS no Microsoft Windows ou scripts de shell ".
Harman

24

Você também pode usar a biblioteca executiva do Apache Commons .

Exemplo:

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

Para referência adicional, também é fornecido um exemplo no Apache Doc .


Posso executar isso no Windows?
bekur

@KisHanSarsecHaGajjar também podemos capturar a saída do shellscript e exibi-la na interface do usuário java. Eu quero saber o que é possível fazê-lo
Kranthi Sama

@KranthiSama Você pode configurar OutputStreampara DefaultExecuterusar DefaultExecuter.setStreamHandlermétodo para saída de captura em OutputStream. Por favor, consulte esta discussão para mais informações: Como posso capturar a saída de um comando ...
Não é um bug

1
Link para adicionar a dependência da biblioteca do Apache Commons Exec ao seu projeto - commons.apache.org/proper/commons-exec/dependency-info.html
aunlead

2
a melhor solução.
Dev

23

Eu diria que não está no espírito do Java executar um script de shell a partir do Java. O Java deve ser multiplataforma, e a execução de um shell script limitaria seu uso a apenas UNIX.

Com isso dito, é definitivamente possível executar um shell script a partir do Java. Você usaria exatamente a mesma sintaxe listada (eu ainda não tentei, mas tente executar o shell script diretamente e, se isso não funcionar, execute o próprio shell, passando o script como parâmetro de linha de comando) .


43
Sim, mas de muitas maneiras esse "espírito de Java" ou "escrever uma vez é executado em todos os lugares" é um mito de qualquer maneira.
BobbyShaftoe

15
O que é mítico nisso?
8113 Chris Ballance

15
o que é mítico é que você geralmente acaba escrevendo inúmeras switchese ifinstruções para contornar todas as nuances que não funcionam exatamente da mesma maneira em plataformas diferentes, apesar dos melhores esforços das pessoas que criaram as bibliotecas principais do java.
Brian Sweeney

2
Surpreendentemente! Eu concordo com todos os comentários acima e a resposta!
AnBisw

11
@BobbyShaftoe Eu tenho escrito java por 16 anos, e sempre desenvolvido em janelas, todos os meus aplicativos sempre implantado em solaris / IBM ou Oracle sabor caixas UNIX, então eu não tenho idéia do que você está falando
Kalpesh Soni

21

Eu acho que você respondeu sua própria pergunta com

Runtime.getRuntime().exec(myShellScript);

Quanto à boa prática ... o que você está tentando fazer com um shell script que não pode fazer com Java?


Eu enfrentei uma situação semelhante em que preciso sincronizar alguns arquivos em servidores diferentes quando uma determinada condição ocorre no meu código java. Existe alguma outra maneira melhor?
v Kumar

1
@ Chris Ballance ... Eu sei que esse comentário é quase depois de 10 anos :) mas, para responder à sua pergunta, e se meu programa tiver que interagir com meia dúzia de canais a jusante e a montante e dependente do modo de comunicação aceito. Especialmente quando você está trabalhando em um projeto que interage com tantos canais ímpares :)
Stunner

Enviar a funcionalidade para um script de shell seria um esforço de última hora, se não houver outra maneira de fazer o trabalho em Java. Será necessariamente complicado coordenar estado e dependências se você estiver enviando o trabalho para um script de shell. Ocasionalmente, um script de shell é a única maneira ou período de tempo que o torna a única maneira razoável de realizar um pouco de trabalho; portanto, essa é uma maneira de fazer isso.
Chris Ballance

11

Sim, é possível fazer isso. Isso funcionou para mim.

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

Aqui está o meu exemplo. Espero que faça sentido.

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

Sim, é possível e você já respondeu! Sobre boas práticas, acho melhor iniciar comandos a partir de arquivos e não diretamente do seu código. Então você tem que fazer Java executar a lista de comandos (ou um comando) em uma já existente .bat, .sh, .ksh... arquivos. Aqui está um exemplo de execução de uma lista de comandos em um arquivo MyFile.sh:

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

4

Para evitar a necessidade de codificar um caminho absoluto, você pode usar o método a seguir que encontrará e executará seu script se ele estiver no diretório raiz.

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

Quanto a mim, todas as coisas devem ser simples. Para executar o script, basta executar

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

3

A biblioteca do ZT Process Executor é uma alternativa ao Apache Commons Exec. Possui funcionalidade para executar comandos, capturar sua saída, definir tempos limites etc.

Ainda não o usei, mas parece razoavelmente bem documentado.

Um exemplo da documentação: Executando um comando, bombeando o stderr para um criador de logs, retornando a saída como sequência UTF8.

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

Sua documentação lista as seguintes vantagens sobre o Commons Exec:

  • Manuseio aprimorado de fluxos
    • Leitura / gravação em fluxos
    • Redirecionando stderr para stdout
  • Manipulação aprimorada de tempos limite
  • Verificação aprimorada dos códigos de saída
  • API aprimorada
    • Um liners para casos de uso bastante complexos
    • Forros um para obter a saída do processo em uma String
    • Acesso ao objeto Process disponível
    • Suporte para processos assíncronos ( futuro )
  • Log aprimorado com a API SLF4J
  • Suporte para múltiplos processos

2

Aqui está um exemplo de como executar um script bat / cmd do bash do Unix ou do Windows a partir de Java. Os argumentos podem ser transmitidos no script e a saída recebida do script. O método aceita um número arbitrário de argumentos.

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

Ao executar no Unix / Linux, o caminho deve ser semelhante ao Unix (com '/' como separador), ao executar no Windows - use '\'. Hier é um exemplo de um script bash (test.sh) que recebe um número arbitrário de argumentos e dobra todos os argumentos:

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

Ao ligar

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

no Unix / Linux, a saída é:

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 é um script cmd simples do Windows test.cmd que conta o número de argumentos de entrada:

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

Ao chamar o script no Windows

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

A saída é

Received from script: 3 arguments received

1

É possível, apenas executá-lo como qualquer outro programa. Apenas verifique se o seu script possui o # apropriado! (she-bang) como a primeira linha do script e verifique se há permissões de execução no arquivo.

Por exemplo, se for um script bash, coloque #! / Bin / bash na parte superior do script, também chmod + x.

Além disso, se for uma boa prática, não, não é, especialmente para Java, mas se você economizar muito tempo exibindo um script grande e não receber um pagamento extra por isso;) economize seu tempo; script e coloque a portabilidade para Java na sua lista de tarefas de longo prazo.


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

Esta é uma resposta tardia. No entanto, pensei em me esforçar para conseguir que um script shell fosse executado a partir de um aplicativo Spring-Boot para futuros desenvolvedores.

  1. Eu estava trabalhando no Spring-Boot e não consegui encontrar o arquivo a ser executado no meu aplicativo Java e estava sendo lançado FileNotFoundFoundException. Eu tive que manter o arquivo no resourcesdiretório e tive que definir o arquivo para ser verificado pom.xmlenquanto o aplicativo estava sendo iniciado da seguinte maneira.

    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <includes>
                <include>**/*.xml</include>
                <include>**/*.properties</include>
                <include>**/*.sh</include>
            </includes>
        </resource>
    </resources>
  2. Depois disso, tive problemas para executar o arquivo e ele estava retornando error code = 13, Permission Denied. Então eu tive que tornar o arquivo executável executando este comando -chmod u+x myShellScript.sh

Finalmente, eu poderia executar o arquivo usando o seguinte trecho de código.

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

Espero que isso resolva o problema de alguém.


0

Da mesma forma que o Solaris 5.10 funciona assim, ./batchstart.shhá um truque que não sei se o seu sistema operacional aceita o uso \\. batchstart.sh. Essa barra dupla pode ajudar.


0

Eu acho que com

System.getProperty("os.name"); 

A verificação do sistema operacional pode gerenciar os scripts shell / bash, se houver suporte. se houver necessidade de tornar o código portátil.


0

para uso em 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);
    }
}

e para uso;

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

OU

ShellAssistance.runShell("", "pg_dump", new String[]{"-U", "aliAdmin", "-f", "/home/Backup.sql", "StoresAssistanceDB"}, new Hashmap<>());
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.