Java でシェル コマンドを実行する
On 10月 23, 2021 by adminはじめに
今回は、Runtime
と ProcessBuilder
クラスを活用して、Java でシェル コマンドとスクリプトを実行する方法を紹介します。
私たちは日々の仕事で多くのものを自動化するためにコンピュータを使っています。 システム管理者は常に多くのコマンドを実行しますが、その中には非常に反復的で、実行の合間に最小限の変更しか必要としないものもあります。 すべてを手動で実行する必要はありません。 Java を使用して、単一または複数のシェル コマンドの実行、シェル スクリプトの実行、ターミナル/コマンド プロンプトの実行、作業ディレクトリの設定、コア クラスによる環境変数の操作などが可能です。
Runtime.exec()
The Runtime
クラス in Java は高レベル クラスで、すべての Java アプリケーションで存在するものです。
getRuntime()
メソッドを介してアプリケーションに関連付けられたランタイムを抽出することにより、exec()
メソッドを使用して直接コマンドを実行したり、.bat
/.sh
ファイルを実行することができます。
exec()
メソッドにはいくつかのオーバーロードされたバリエーションがあります:
-
public Process exec(String command)
–command
に含まれるコマンドを別のプロセスで実行します。 -
public Process exec(String command, String envp)
–command
を、環境変数の配列と一緒に実行します。 -
public Process exec(String command, String envp, File dir)
–dir
ディレクトリの中から指定された環境変数とともにcommand
を実行する. -
public Process exec(String cmdArray)
– 文字列の配列の形式でコマンドを実行する. -
public Process exec(String cmdarray, String envp, File dir)
–dir
ディレクトリ内から環境変数を指定してコマンドを実行する。
これらの処理はインタープリタから外部で実行されるため、システムに依存することは特筆すべきだろう。 これらは同じことを実現します。 command
はいずれにせよ配列に分解されるので、この2つのどれを使っても同じ結果になるはずです。
exec("dir /folder")
とexec(new String{"dir", "/folder"}
のどちらを使うかはあなた次第です。
これらのオーバーロードされたメソッドが互いにどう違うか、いくつかの例を書いて見ましょう。
String からコマンドを実行する
これらの 3 つのうち、最も単純な方法から始めましょう:
Process process = Runtime.getRuntime().exec("ping www.stackabuse.com");
このコードを実行すると、String 形式で提供されたコマンドが実行されます。 しかし、これを実行しても何も表示されません。
これが正しく実行されたかどうかを検証するために、process
オブジェクトを取得したいと思うでしょう。
public static void printResults(Process process) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); String line = ""; while ((line = reader.readLine()) != null) { System.out.println(line); }}
ここで、exec()
メソッドの後にこのメソッドを実行すると、次のような結果が得られるはずです:
Pinging www.stackabuse.com with 32 bytes of data:Reply from 104.18.57.23: bytes=32 time=21ms TTL=56Reply from 104.18.57.23: bytes=32 time=21ms TTL=56Reply from 104.18.57.23: bytes=32 time=21ms TTL=56Reply from 104.18.57.23: bytes=32 time=21ms TTL=56Ping statistics for 104.18.57.23: Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),Approximate round trip times in milli-seconds: Minimum = 21ms, Maximum = 21ms, Average = 21ms
他の例でも、Process
インスタンスからプロセス情報を抽出する必要があることを覚えておいてください。
作業ディレクトリの指定
例えば、あるフォルダからコマンドを実行したい場合、次のようにします:
Process process = Runtime.getRuntime() .exec("cmd /c dir", null, new File("C:\Users\")); //.exec("sh -c ls", null, new File("Pathname")); for non-Windows usersprintResults(process);
ここでは、exec()
メソッドにcommand
、新しい環境変数用のnull
、作業ディレクトリとして設定するnew File()
を用意しています。
注目すべきは、dir
などのコマンドの前にcmd /c
が追加されている点です。
Windowsで作業しているので、これでcmd
が開き、/c
がその後のコマンドを実行します。 この場合、dir
です。
なぜ、ping
の例では必須ではなかったのに、この例では必須なのかは、SOユーザーがうまく答えてくれていますね。
Running the previous piece of code will result in:
Volume in drive C has no label. Volume Serial Number is XXXX-XXXX Directory of C:\Users08/29/2019 05:01 PM <DIR> .08/29/2019 05:01 PM <DIR> ..08/18/2016 09:11 PM <DIR> Default.migrated08/29/2019 05:01 PM <DIR> Public05/15/2020 11:08 AM <DIR> User 0 File(s) 0 bytes 5 Dir(s) 212,555,214,848 bytes free
Let’s look at how we could supply the previous command in several individual parts, instead of a single String:
Process process = Runtime.getRuntime().exec( new String{"cmd", "/c", "dir"}, null, new File("C:\Users\")); printResults(process);
Running this piece of code will also result in.ここで、このコードを実行すると、結果はどうなるか見てみましょう。
Volume in drive C has no label. Volume Serial Number is XXXX-XXXX Directory of C:\Users08/29/2019 05:01 PM <DIR> .08/29/2019 05:01 PM <DIR> ..08/18/2016 09:11 PM <DIR> Default.migrated08/29/2019 05:01 PM <DIR> Public05/15/2020 11:08 AM <DIR> User 0 File(s) 0 bytes 5 Dir(s) 212,542,808,064 bytes free
結局のところ、単一の文字列または文字列配列を使用するアプローチにかかわらず、入力したコマンドは常に配列に分解された後、基本ロジックで処理されます。
環境変数の使用
環境変数をどのように使用するか見ていきましょう。 ここでは、echo
を使用して var1
の値を出力しています。
このコードを実行すると、次の結果が得られます:
value1
Running .bat and .sh Files
時には、すべてをファイルにオフロードして、プログラム的に追加する代わりにそのファイルを実行する方がずっと簡単なことがあります。
echo Hello World
次に、前と同じ方法で作成します。
Process process = Runtime.getRuntime().exec( "cmd /c start file.bat", null, new File("C:\Users\User\Desktop\"));
これでコマンドプロンプトが開き、設定した作業ディレクトリで .bat
ファイルが実行されます。
このコードを実行すると、確かに次の結果が得られます:
オーバーロードされた exec()
署名のすべてが完了したので、ProcessBuilder
クラスとそれを使用してコマンドを実行する方法について見ていきましょう。
ProcessBuilder
ProcessBuilder
は、Runtime.getRuntime().exec()
メソッドを使用するときにコマンドを実行する基本的なメカニズムです。
/** * Executes the specified command and arguments in a separate process with * the specified environment and working directory. *...*/public Process exec(String cmdarray, String envp, File dir) throws IOException { return new ProcessBuilder(cmdarray) .environment(envp) .directory(dir) .start();}
JavaDocs for the Runtime
クラス
が exec()
メソッドの入力を受けてコマンドを実行する方法を見て、同様にそれを使用する方法についても良いアイデアを得られました。
これは String cmdarray
を受け取り、実行するにはそれで十分です。 あるいは、String envp
や File dir
などのオプションの引数を与えることもできます。
これらのオプションを調べてみましょう。
ProcessBuilder: Strings からコマンドを実行する
cmd /c dir
のような単一の String を提供できる代わりに、この場合、それを分割する必要があります。 たとえば、以前のように C:/Users
ディレクトリにあるファイルをリストアップしたい場合、次のようにします:
ProcessBuilder processBuilder = new ProcessBuilder();processBuilder.command("cmd", "/c", "dir C:\Users");Process process = processBuilder.start();printResults(process);
実際に Process
を実行するには、start()
コマンドを実行して返された値を Process
インスタンスに割り当てます。
このコードを実行すると:
Volume in drive C has no label. Volume Serial Number is XXXX-XXXX Directory of C:\Users08/29/2019 05:01 PM <DIR> .08/29/2019 05:01 PM <DIR> ..08/18/2016 09:11 PM <DIR> Default.migrated08/29/2019 05:01 PM <DIR> Public05/15/2020 11:08 AM <DIR> User 0 File(s) 0 bytes 5 Dir(s) 212,517,294,080 bytes free
しかしこの方法は以前のものよりも優れているとは言えません。 ProcessBuilder
クラスで便利なのは、カスタマイズが可能なことです。 コマンドだけでなく、プログラム的に設定することができます。
ProcessBuilder: 作業ディレクトリを指定する
コマンドで作業ディレクトリを指定する代わりに、プログラム的に設定します。
processBuilder.command("cmd", "/c", "dir").directory(new File("C:\Users\"));
ここで、作業ディレクトリを以前と同じに設定しましたが、その定義をコマンド自体から移動しました。 このコードを実行すると、前回の例と同じ結果が得られます。
ProcessBuilder: 環境変数
ProcessBuilder
のメソッドを使用すると、Map
の形式で環境変数のリストを取得することが簡単にできます。
現在利用可能な環境変数を取得し、後で使用するためにいくつか追加してみましょう:
ProcessBuilder processBuilder = new ProcessBuilder();Map<String, String> environmentVariables = processBuilder.environment();environmentVariables.forEach((key, value) -> System.out.println(key + value));
ここでは、返された環境変数を Map
に詰め、forEach()
を実行してコンソールに値を出力しています。
このコードを実行すると、あなたのマシンにある環境変数のリストが得られます:
DriverDataC:\Windows\System32\Drivers\DriverDataHerokuPathE:\HerokuProgramDataC:\ProgramData...
では、そのリストに環境変数を追加して使ってみましょう:
environmentVariables.put("var1", "value1");processBuilder.command("cmd", "/c", "echo", "%var1%");Process process = processBuilder.start();printResults(process);
このコードを実行すると:
value1
もちろん、プログラムの実行が終了したら、この変数はリストに残りません:
ProcessBuilder: .bat および .sh ファイルを実行する
ファイルを実行したい場合は、再度、ProcessBuilder
インスタンスに必要な情報を提供します。 そのためにRuntime
クラスとProcessBuilder
クラスを使用しました。
Java を使用すると、単一または複数のシェルコマンドの実行、シェルスクリプトの実行、ターミナル/コマンドプロンプトの実行、作業ディレクトリの設定、コアクラスによる環境変数の操作などを行うことができます。
コメントを残す