我从Java(1.6)设置Linux环境有一个奇怪的问题;特别是“PATH”变量。
简而言之,我有一个用于运行本机进程的管道,它使用java.lang.ProcessBuilder
。用户可以选择通过名为HashMap
的{{1}}
environment
如果我将ProcessBuilder pb = new ProcessBuilder(args);
Map<String, String> env = pb.environment();
if (environment != null)
env.putAll(environment);
Process process = pb.start();
变量转储到控制台,并且PATH变量的值正确,则env
变量设置正确。但是,运行该进程会导致抛出Exception
:
java.io.IOException: error=2, No such file or directory
在终端shell中使用相同的环境变量运行相同的进程。为了测试这个,我在终端后设置环境后运行Eclipse。在这种情况下,ProcessBuilder
进程正确运行。
所以必须发生的是ProcessBuilder
没有使用我为它设置的环境,而是使用当前的系统环境。
我在网上找不到任何满意的答案。也许这是特定于操作系统的问题?或者其他我想念的东西?
答案 0 :(得分:13)
我认为这不是一个错误,我认为你理解环境变量的边界和角色是一个问题。 ProcessBuilder.environment()
包含对生成的进程“进程本地”的环境变量。它们不是系统范围的,也不是登录范围的,它们甚至不会影响运行ProcessBuilder的环境。
ProcessBuilder.environment()
地图包含由衍生流程仅的流程本地变量。显然,生成处理看到ProcessBuilder.environment()
的先决条件是成功生成过程,这是我认为你甚至没有达到的一点。
据我所知,(从Java)修改当前正在运行的进程'PATH是不可能的,这是我认为你期望发生的(或者能够做到的)。所以我认为您必须将ProcessBuilder指向您尝试启动的可执行文件的完全限定路径(或者确保在您启动将使用ProcessBuilder的JVM之前已正确设置PATH,这是您在'工作中所做的' '在启动IDE之前在终端中设置它的方案。)
答案 1 :(得分:7)
在Linux上:
String path = System.getenv("HOME");
ProcessBuilder pb = new ProcessBuilder("/bin/bash","-c","export PATH=" +
"PATH-TO-ADD" + ":" + path + " && exec");
在这种情况下,PATH
变量根据需要进行更新,并在新$PATH
中搜索可执行文件。这在Linux上适用于我。
答案 2 :(得分:6)
您需要了解环境变量是过程上下文的本地变量。新进程获取父级环境的副本,但每个副本都是独立的。父母的变化不会影响现有的孩子(只有新孩子),孩子的变化不会影响父母的父母或新孩子。
在您的情况下,Java进程创建子进程并将修改后的PATH
变量放入子进程的上下文中。这不会影响Java进程。子进程不是shell,因此忽略PATH
变量。该过程直接使用OS服务创建。这些内容将查看包含旧PATH
变量的Java进程的上下文,除非您在启动Java进程之前更改shell 中的环境。
要解决您的问题,您有两种选择:
检查Java中的PATH
变量,将其拆分为路径元素并手动搜索可执行文件。然后,您可以使用绝对路径调用ProcessBuilder
,并将新的PATH
放入子项中,这样孙子将拥有正确的路径。
调用shell以启动子进程。 shell将使用它的路径(您可以通过环境传递)。
第二种情况是这样的:
PATH
。"sh", "-c", "cmd args"
或"cmd.exe", "/c", "cmd args"
)PATH
并运行正确的命令。第二种情况的缺点是你必须正确地转义和/或引用命令的参数(args
),否则空格和其他特殊字符会引起问题。
答案 3 :(得分:1)
ProcessBuilder javadoc清楚的一点是,您可以使用environment()方法获取环境变量,然后修改返回的地图。从该ProcessBuilder实例启动的任何后续进程都将进行更改。
答案 4 :(得分:0)
我认为你是对的。当前正在执行的java代码将不使用您正在为正在执行的子进程准备的环境变量。您可以创建一个中间可执行文件或脚本,您可以将变量传递给它并让它执行您的程序。
答案 5 :(得分:0)
这似乎是java和外部进程的真正问题
以下是Windows 7和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'(例如废话,我的电脑上没有名为xbox的目录)< / p>
仅适用于协议:
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", "/?"});
请注意我的环境太小了,因为我取代了原生环境。这意味着整个程序正好具有这两个环境变量。