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?

Lii
fonte
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();
Milhous
fonte
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 .

Não é um bug
fonte
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) .

Jack Leow
fonte
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?

Chris Ballance
fonte
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());
        }
    }
Desta Haileselassie Hagos
fonte
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;
}
Lionel Yan
fonte
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);
IANQUE
fonte
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!");
    }
}
anataliocs
fonte
3

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

new ProcessBuilder("pathToYourShellScript").start();
Vladimir Bosyi
fonte
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
Lii
fonte
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
Andrushenko Alexander
fonte
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.

Kekoa
fonte
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();
  }  
Girdhar Singh Rathore
fonte
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.

Reaz Murshed
fonte
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.

Saul
fonte
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.

DayaMoon
fonte
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<>());
Ali Bagheri
fonte