JavaコードからUnixシェルスクリプトを実行する方法


155

JavaからUnixコマンドを実行するのは非常に簡単です。

Runtime.getRuntime().exec(myCommand);

しかし、JavaコードからUnixシェルスクリプトを実行することは可能ですか?はいの場合、Javaコード内からシェルスクリプトを実行することをお勧めしますか?


3
そのシェルスクリプトが対話型である場合、物事は面白くなります。
ernesto 2014

myCommand変数とは何ですか?はいの場合、機能しません。execメソッドにはString []と引数が必要です。以下のanswarを参照してください。完全に機能します
Girdhar Singh Rathore

回答:


174

あなたは本当にプロセスビルダーを見るべきです。それは本当にこの種のもののために構築されています。

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からスクリプトを呼び出すのは良い習慣ですか?パフォーマンスの問題はありますか?
kautuksahni 2017

1
Javaの構成によっては、/ bin / bashまたはshを指定してスクリプトを実行する必要があることに注意してください(stackoverflow.com/questions/25647806/…を参照)
Ben Holland

@Milhousこれはかなり遅いことであり、状況が変わった可能性がありますが、現在のJavaプロセスのドキュメントでは、これはシェルスクリプトの推奨されない方法です:docs.oracle.com/javase/8/docs/api/java/lang/Process.html "プロセスを作成するメソッドは、ネイティブウィンドウプロセス、デーモンプロセス、Microsoft Windows上のWin16 / DOSプロセス、シェルスクリプトなど、特定のネイティブプラットフォーム上の特別なプロセスではうまく機能しない場合があります。
Harman、

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 14

@KisHanSarsecHaGajjarでは、シェルスクリプトからの出力をキャプチャして、Javaのuiに表示することもできます。私はそうすることが可能かどうか知りたい
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
はい、しかし多くの意味で、「Javaの精神」または「どこでもどこでも実行する」というマントラは、とにかく神話です。
BobbyShaftoe 2009

15
それについての神話は何ですか?
Chris Ballance、

15
神話的なのは、Javaコアライブラリを考案した人たちの最善の努力にもかかわらず、通常、さまざまなプラットフォームでまったく同じように機能しないすべてのニュアンスを回避するために、多数のswitchesand ifステートメントを書くことになるということです。
ブライアンスウィーニー、

2
意外と十分!上記のすべてのコメントと回答に同意します。
AnBisw

11
@BobbyShaftoe I私はあなたが話しているかわかりませんので、16年のJavaを書いて、常にWindows上で開発されている、すべての私のアプリは常にSolaris上で展開/ IBMやOracleは、UNIXボックスを風味
Kalpesh曽爾

21

あなたはあなた自身の質問に答えました

Runtime.getRuntime().exec(myShellScript);

それが良い習慣であるかどうかについて...あなたはJavaではできないシェルスクリプトで何をしようとしていますか?


私は、Javaコードで特定の条件が発生したときに、異なるサーバー上のいくつかのファイルをrsyncする必要がある同様の状況に直面しました。他にもっと良い方法はありますか?
v kumar

1
@Chris Ballance ...私はこのコメントがほぼ10年後であることを知っています:)しかし、あなたの質問に答えるために、私のプログラムが6ダースのダウンストリームおよびアップストリームチャネルと対話し、それらの受け入れられた通信モードに依存する必要がある場合はどうでしょうか。特に、非常に多くの奇妙なチャンネルとやり取りするプロジェクトに取り組んでいるときは:)
Stunner

機能をシェルスクリプトにプッシュするのは、Javaで作業する方法が他にない場合の最後の努力です。シェルスクリプトに作業をプッシュする場合、状態と依存関係を調整することは必然的に複雑になります。たまに、シェルスクリプトが唯一の方法またはタイムフレームがそれを少しの作業を達成するための唯一の合理的な方法にするので、これはそれを行う方法です。
Chris Ballance

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は、コマンドのリスト(または1つのコマンド)を実行させる必要がありますので.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をstdoutにリダイレクトする
  • タイムアウトの処理の改善
  • 終了コードのチェックの改善
  • 改善されたAPI
    • 非常に複雑なユースケースのためのワンライナー
    • プロセス出力を文字列に取得する1つのライナー
    • 利用可能なProcessオブジェクトへのアクセス
    • 非同期プロセスのサポート(Future
  • SLF4J APIによるロギングの改善
  • 複数のプロセスのサポート

2

JavaからUnix bashまたはWindows bat / cmdスクリプトを実行する方法の例を次に示します。引数をスクリプトに渡し、スクリプトから出力を受け取ることができます。このメソッドは、任意の数の引数を受け入れます。

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は、任意の数の引数を受け取り、すべての引数を2倍にする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は、入力引数の数をカウントする単純なcmd Windowsスクリプト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への移植を長期的なToDoリストに追加します。


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ですが、OSが\\. 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<>());
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.