java:自分自身の絶対パスを知る方法(その2)

 

時代の流れと共に、と書くと大げさですが、言語仕様が変わると
過去の手法が使えなくなることがあります。

こんにちは、世間の流れ(ゴールデンウィーク)とは逆に
遊びに行かずにプログラミングしている管理人イガジーです。

過去に自分自身の絶対パスを知る方法という記事を書いたのですが
Java(VM)が新しくなったせいでしょうね、Eclipse上から動かす時に
うまくいかなくなっているのに(今頃ですが)気付きました。
jarにして動かすぶんには問題ないようです。

System.getProperty(“java.class.path”);
で、返ってくる文字列が、以前は実行時のパス、すなわち
/home/igazeey/workspace/ProjectName/bin/
みたいな形だった(と思う)のですが、
/opt/oracle-jdk-bin-1.8.0.162/jre/lib/resources.jar:
/opt/oracle-jdk-bin-1.8.0.162/jre/lib/rt.jar:
/opt/oracle-jdk-bin-1.8.0.162/jre/lib/jsse.jar:
/opt/oracle-jdk-bin-1.8.0.162/jre/lib/jce.jar:
/opt/oracle-jdk-bin-1.8.0.162/jre/lib/charsets.jar:
/opt/oracle-jdk-bin-1.8.0.162/jre/lib/ext/dnsns.jar:
/opt/oracle-jdk-bin-1.8.0.162/jre/lib/ext/sunec.jar:
/opt/oracle-jdk-bin-1.8.0.162/jre/lib/ext/zipfs.jar:
/opt/oracle-jdk-bin-1.8.0.162/jre/lib/ext/nashorn.jar:
/opt/oracle-jdk-bin-1.8.0.162/jre/lib/ext/jfxrt.jar:
/opt/oracle-jdk-bin-1.8.0.162/jre/lib/ext/sunpkcs11.jar:
/opt/oracle-jdk-bin-1.8.0.162/jre/lib/ext/cldrdata.jar:
/opt/oracle-jdk-bin-1.8.0.162/jre/lib/ext/sunjce_provider.jar:
/opt/oracle-jdk-bin-1.8.0.162/jre/lib/ext/jaccess.jar:
/opt/oracle-jdk-bin-1.8.0.162/jre/lib/ext/localedata.jar:
/home/igazeey/workspace/ProjectName/bin
のような文字列が返ってきます。
※ 見やすくするためにパス区切りマーク : で改行していますが
※ 実際は1行です

実際の .class ファイルは
/home/igazeey/workspace/ProjectName/bin/packagename/
に置かれているので、単純に最後の
/home/igazeey/workspace/ProjectName/bin
だけを取り出せば済むというわけにも行かない感じです。

で、どうしようか、としばし悩みましたが、以下のように
リソースを取得するのと同じように URL を使ってみました。

package exam;
import java.net.URL;

public class ExecPath {
	public ExecPath(String myname) { // constructor

		String classname=myname+".class";
		URL url=this.getClass().getResource(classname);
		if (url==null) {
			System.out.println("url==null. Maybe classname is wrong , abort.");
			System.exit(1);
		}
		String pathstr=url.toString();
		String fsp=System.getProperty("file.separator");
		System.out.println("url.toString()="+pathstr);
		if (pathstr.substring(0,4).equals("jar:")) {
			// jar:file:/usr/local/jar/execpath.jar!/exam/ExecPath.class
			System.out.println("jar file");
			pathstr=pathstr.substring(4);
			int p=pathstr.indexOf("!");
			if (p>=0) {
				System.out.println("remove right string of !");
				pathstr=pathstr.substring(0,p);
				System.out.println("pathstr="+pathstr);
			}
			System.out.println("remove xxxxx.jar");
			int q;
			for (p=0;(q=pathstr.indexOf(fsp,p))>=0; p=q+1);
//			System.out.println("last fsp="+p);
			pathstr=pathstr.substring(0,p);
		}else{
			// jar ファイルではない時
			// file:/home/igazeey/workspace/ExecPath/bin/exam/ExecPath.class
			if (pathstr.endsWith(classname)) {
				System.out.println("remove classname");
				pathstr=pathstr.substring(0,pathstr.length()-classname.length());
			}
		}
		System.out.println("pathstr="+pathstr);

		if (pathstr.substring(0,5).equals("file:")) {
			System.out.println("remove file:");
			pathstr=pathstr.substring(5);
		}
		System.out.println("pathstr="+pathstr);

	}
	
	public static void main(String[] args) {
		new ExecPath("ExecPath");
	}
}

動きがわかりやすいように、System.out.println(); で途中経過を表示して
いますが、運用上は削除してご利用ください。

簡単に説明しますと、まず「必ず存在するファイル」として自分自身の
クラスファイル、すなわち”ExecPath.class” の URL を
getClass().getResource(classname);
で取得しています。

Eclipse上で動かすと URL は
file:/home/igazeey/workspace/ExecPath/bin/exam/ExecPath.class
になり、jar にして動かすと
jar:file:/usr/local/jar/execpath.jar!/exam/ExecPath.class
になります。

あとは、”file:”とか”ExecPath.class” や
“jar:” とか “execpath.jar!/exam/ExecPath.class” を取り除けば
実行時のディレクトリが得られます。

ちなみに、これを使って実行時ディレクトリに何か書き込みを行う場合は
そこ(例えば /usr/local/jar/)に書き込み権限を与えておく必要が
あります。

Linuxで確認しましたが Windowsや MACでは未確認なので
(たぶん大丈夫だと思うのですが)うまく行かなかった時はコメントを
お願いします。

この記事へのコメントはこちら

メールアドレスは公開されませんのでご安心ください。
また、* が付いている欄は必須項目となりますので、必ずご記入をお願いします。

内容に問題なければ、下記の「コメント送信」ボタンを押してください。