1. ホーム
  2. Java

シェルコマンドやスクリプトのJavaコール

2022-02-08 17:37:43

1. はじめに

LinuxでJavaプログラムを実行するとき、シェルコマンドやスクリプトを呼び出す必要があることがあります。そして、Runtime.getRuntime().exec()メソッドはこの機能を提供してくれます。Runtime.getRuntime()では、以下の種類のexec()メソッドが提供されています。

Process exec(String command) 
Executes the specified string command in a separate process. 

Process exec(String[] cmdarray) 
Executes the specified command and variables in a separate process. 

Process exec(String[] cmdarray, String[] envp) 
Executes the specified commands and variables in a separate process of the specified environment. 

Process exec(String[] cmdarray, String[] envp, File dir) 
Executes the specified commands and variables in a separate process of the specified environment and working directory. 

Process exec(String command, String[] envp) 
Executes the specified string command in a separate process of the specified environment. 

Process exec(String command, String[] envp, File dir) 
Executes the specified string command in a separate process with the specified environment and working directory. 





実際,cmdarrayはcommandと似ていますが,パラメータにenvpパラメータがないかnullに設定されている場合,呼び出したコマンドは現在のプログラムが実行されている環境で実行されることを意味し,dirパラメータがないかnullに設定されている場合,呼び出しコマンドは現在のプログラムを実行しているディレクトリで実行されることを意味するので,他のディレクトリのファイルやスクリプトは絶対パスで呼んだ方がよいでしょう.各パラメータの意味は次のとおりです.

  1. cmdarray。呼び出されたコマンドとその引数を含む配列。 
  2. コマンドを使用します。指定されたシステムコマンド。
  3. envp: 環境変数の各要素を name=value の形式で設定した文字列の配列。子プロセスが現在のプロセスの環境を継承すべき場合、このパラメータは null になる。
  4. dir: 子プロセスが現在のプロセスの作業ディレクトリを継承すべき場合、このパラメータはNULLになります。 

注意深い読者は、呼び出し操作を実行するために、JVMがプロセスを開始することに気づきます。したがって、プロセスクラスの次のメソッドを呼び出すことによって、呼び出し操作が正しく実行されたかどうかを知ることができます。

abstract int waitFor() 
Causes the current thread to wait, if necessary, until the process represented by this Process object has terminated. 





プロセスの終了値。慣習上、0は正常終了を、それ以外は異常終了を表す。

また、特定のシェルコマンドやスクリプトを呼び出すと戻り値があるので、その戻り値や出力をキャプチャしたらどうでしょうか?この問題を解決するために、Processクラスは提供しています。

abstract InputStream getInputStream() 
Gets the input stream of the child process. It is better to buffer the input stream.

2. シェルコマンドを起動する

ここでは、説明のためにtarコマンドだけを使います。tarは、圧縮せずにパッケージングするコマンドです。また、tarの呼び出しがきちんと実行されているかどうかを確認するために、waitFor()メソッドを呼び出すことにします。

private void callCMD(String tarName, String fileName, String... workspace){
	try {
		String cmd = "tar -cf" + tarName + " " + fileName;
// String[] cmd = {"tar", "-cf", tarName, fileName};
		File dir = null;
		if(workspace[0] ! = null){
			dir = new File(workspace[0]);
			System.out.println(workspace[0]);
		}
		process = Runtime.getRuntime().exec(cmd, null, dir);
// process = Runtime.getRuntime().exec(cmd);
		int status = process.waitFor();
		if(status ! = 0){
			System.err.println("Failed to call shell's command and the return status's is: " + status);
		}
	}
	catch (Exception e){
		e.printStackTrace();
	}
}





注:コマンドをString[]に入れる場合、コマンドの各部分はString[]の中に要素として存在するか、コマンドをスペース文字で分割して得られるString[]に存在しなければなりません。

String[] cmd = {"tar", "-cf", tarName, fileName}; //right
String[] cmd = {"tar -cf", tarName, fileName}; //error

dirパラメータが何をするか説明するために、特にJavaプログラムをパッケージ化されるディレクトリであるhive/とは別のディレクトリに置いてみました。

/root/workspace/eclipse/Test/src/edu/wzm/CallShell.java
/root/experiment/hive






dirを設定しないか、dirをnullに設定すると、fileNameは相対パスでなければならず、できれば絶対パスでなければなりません。

call.callCMD("/root/experiment/hive.tar", "/root/experiment/hive", null);
// OR
call.callCMD("/root/experiment/hive.tar", "/root/experiment/hive");





もし、dirがhiveのある親ディレクトリを指すように設定していたら、もっと簡単だったでしょう。

call.callCMD("hive.tar", "hive", "/root/experiment/");

3. シェルスクリプトの呼び出し

Javaは、Shellスクリプトを呼び出すのと全く同じ方法で、Shellコマンドを呼び出します。ここでは、いくつかの追加的な側面について説明します。

  1. スクリプトにパラメータを渡す。
  2. 呼び出しの出力をキャプチャする。
  3. envpの使用について。

スクリプトに引数を渡すのは簡単で、呼び出したコマンドに引数を追加してString(またはString[])を形成するだけです。

呼び出しの出力のキャプチャは、前述したように Process.getInputStream() で行います。しかし、入力ストリームをバッファリングすることが推奨されます。

BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));





また、envpはString[]で、String[]の各要素は: name=valueの形式です。例えば、私のLinuxシステムには以下の環境変数はありませんが、私のJavaコードではenvpとして記述しています。

val=2
call=Bash Shell





呼び出したいシェルスクリプトは。/root/experiment/test.shです。

#! /usr/bin/env bash

args=1
if [ $# -eq 1 ];then
	args=$1
	echo "The argument is: $args"
fi

echo "This is a $call"
start=`date +%s`
sleep 3s
end=`date +%s`
cost=$((($end - $start) * $args * $val))
echo "Cost Time: $cost"





Javaの呼び出しコードは

private void callScript(String script, String args, String... workspace){
	try {
		String cmd = "sh " + script + " " + args;
// String[] cmd = {"sh", script, "4"};
		File dir = null;
		if(workspace[0] ! = null){
			dir = new File(workspace[0]);
			System.out.println(workspace[0]);
		}
		String[] evnp = {"val=2", "call=Bash Shell"};
		process = Runtime.getRuntime().exec(cmd, evnp, dir);
// process = Runtime.getRuntime().exec(cmd);
		BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));
		String line = "";
		while ((line = input.readLine()) ! = null) {
			System.out.println(line);
		}
		input.close();
	}
	catch (Exception e){
		e.printStackTrace();
	}
}

public static void main(String[] args) {
	// TODO Auto-generated method stub
	CallShell call = new CallShell();
	call.callScript("test.sh", "4", "/root/experiment/");
}






出力します。

/root/experiment/
The argument is: 4
This is a Bash Shell
Cost Time: 24