Linux環境をJava(1.6);具体的には "PATH"変数から設定するという奇妙な問題があります。
一言で言えば、私はJava.lang.ProcessBuilder
を使用するネイティブプロセスを実行するためのパイプラインを持っています。ユーザーはオプションで、HashMap
という名前のenvironment
を介して環境変数を設定できます。
ProcessBuilder pb = new ProcessBuilder(args);
Map<String, String> env = pb.environment();
if (environment != null)
env.putAll(environment);
Process process = pb.start();
env
変数は、コンソールにダンプすると、PATH変数の正しい値で正しく設定されます。ただし、プロセスを実行すると、Exception
がスローされます。
Java.io.IOException: error=2, No such file or directory
同じプロセスは、ターミナルシェルの同じ環境変数で正常に実行されます。これをテストするために、ターミナルで環境を設定した後にEclipseを実行しました。この場合、ProcessBuilder
プロセスは正しく実行されます。
したがって、何が起こっているのかというと、ProcessBuilder
は私が設定した環境ではなく、現在のシステム環境を使用しているということです。
この問題に対する満足のいく答えをオンラインで見つけることができません。おそらくこれはOS固有の問題ですか?または私が欠けている何か他のもの?
バグではないと思います。環境変数の境界と役割を理解することは問題だと思います。 ProcessBuilder.environment()
には、生成されたプロセスに対して「プロセスローカル」になる環境変数が含まれています。これらはシステム全体またはログオン全体ではなく、ProcessBuilderが実行されている環境にも影響しません。
ProcessBuilder.environment()
マップには、生成されたプロセスによって表示されるプロセスローカル変数が含まれていますのみ。明らかに、ProcessBuilder.environment()
を確認することで生成された処理の前提条件は、プロセスの生成が成功することです。これは、あなたが到達しているとは思えない点です。
私の知る限り、現在実行中のプロセスのPATHを(Javaから)変更することは実際には不可能です。これは、あなたが期待している(または実行できる)と思います。したがって、指摘する必要があると思います。起動しようとしている実行可能ファイルへの完全修飾パスへのProcessBuilder(または、ProcessBuilderを使用するJVMを起動する前に、PATHが正しく設定されていることを確認してください。これは、の「作業」シナリオで行ったことです。 IDEを起動する前にターミナルで設定してください)。
Linuxの場合:
String path = System.getenv("HOME");
ProcessBuilder pb = new ProcessBuilder("/bin/bash","-c","export PATH=" +
"PATH-TO-ADD" + ":" + path + " && exec");
この場合、PATH
変数は必要に応じて更新され、実行可能ファイルは新しい$PATH
で検索されます。これはLinuxでうまくいきました。
環境変数はプロセスコンテキストに対してローカルであることを理解する必要があります。新しいプロセスは親の環境のコピーを取得しますが、各コピーは独立しています。親の変更は既存の子(新しい子のみ)には影響せず、子の変更はparentの親または新しい子には影響しません。
あなたの場合、Javaプロセスは子プロセスを作成し、変更されたPATH
変数を子のコンテキストに配置します。これはJava =プロセス。子プロセスはシェルではないため、PATH
変数を無視します。プロセスはOSサービスを使用して直接作成されます。これらは、Javaのコンテキストを調べます。シェルで環境を変更しない限り、古いPATH
変数を含むプロセスbefore Javaプロセスを開始します。
問題を解決するには、2つの選択肢があります。
JavaでPATH
変数を調べ、それをパス要素に分割して、実行可能ファイルを手動で検索します。次に、絶対パスを使用してProcessBuilder
を呼び出すことができますand新しいPATH
を子に配置して、孫が正しいパスを持つようにします。
シェルを呼び出して、子プロセスを開始します。シェルはそのパス(環境を介して渡すことができます)を使用します。
2番目のケースは次のように機能します。
PATH
を使用して環境を作成します。"sh", "-c", "cmd args"
または"cmd.exe", "/c", "cmd args"
)PATH
を見つけて、正しいコマンドを実行します。2番目のケースの欠点は、コマンド(args
)の引数を適切にエスケープまたは引用符で囲む必要があることです。そうしないと、スペースやその他の特殊文字によって問題が発生します。
ProcessBuilder javadocから明らかなことの1つは、environment()メソッドを使用して環境変数を取得し、次にmodify返されたマップを取得できることです。そのProcessBuilderインスタンスから起動された後続プロセスに変更が加えられます。
これは、Javaおよび外部プロセスの実際の問題のようです。
windows7およびJava 7(32ビット)
ProcessBuilder b = new ProcessBuilder();
Map<String, String> env = b.environment();
for (String key : env.keySet())
System.out.println(key + ": " + env.get(key));
を生成します
SystemRoot: C:\Windows
Path: xbox
これは、実行中のプログラム環境とサブプロセス環境に、正確に値 'xbox'を持つパス変数が含まれている必要があることを意味します(たとえば、ナンセンス、私のPCのどこにもxboxという名前のディレクトリはありません)
プロトコルのためだけに:
Map<String, String> env = System.getenv();
for (String key : env.keySet())
System.out.println(key + ": " + env.get(key));
まったく同じ結果が得られます。
私が走るとき
b.command("convert.exe", "/?").inheritIO().start();
このプロセスビルダーと環境で私は得ます
Konvertiert FAT-Volumes in NTFS.
CONVERT Volume /FS:NTFS [/V] [/CvtArea:Dateiname] [/NoSecurity] [/X]
Volume Bestimmt den Laufwerkbuchstaben (gefolgt von einem Doppelpunkt),
den Bereitstellungspunkt oder das Volume.
/FS:NTFS Bestimmt das in NTFS zu konvertierende Volume.
/V Legt fest, dass CONVERT im ausf�hrlichen Modus ausgef�hrt wird.
/CvtArea:Dateiname
Bestimmt die zusammenh�ngende Datei im Stammverzeichnis, die als
Platzhalter f�r NTFS-Systemdateien dienen soll.
/NoSecurity Bestimmt die Sicherheitseinstellungen f�r konvertierte Dateien
und Verzeichnisse, die f�r jeden Benutzer zug�nglich sind.
/X Erzwingt ggf. das Aufheben der Bereitstellung.
Alle ge�ffneten Handles auf das Volume sind in diesem Fall
ung�ltig.
これはの(ドイツ語)出力です
C:\Windows\System32\convert.exe
私が使用するときも同じことが起こります
Runtime.getRuntime().exec(new String[]{"convert.exe", "/?"});
また、ネイティブ環境を置き換えたため、私の環境は非常に小さいことに注意してください。つまり、プログラム全体にこれら2つの環境変数が正確に含まれています。
私はあなたが正しいと思います。現在実行中のJavaコードは、実行中の子プロセス用に準備している環境変数を使用しません。変数を渡して実行させることができる中間実行可能ファイルまたはスクリプトを作成できます。プログラム。