51

是否可以在运行时从 Java 应用程序设置环境变量?在 Java 1.5 java.lang.System 类中有 getenv() 方法,我只需要一个 setenv() 方法...

是否可以修改java进程本身的环境变量;不在子进程中。

是否可以通过JNI来实现?那将如何运作?

谢谢。

编辑:好的,让我这样说 - 我们可以用 Java 做以下事情吗?请回答。

  1. 我们可以修改当前进程的环境吗?
  2. 我们可以修改父进程的环境吗?
  3. 我们可以修改子进程的环境吗?

Hemal Pandya 回答说:“您可以修改当前和子进程的环境,但不能修改生成该进程的父进程的环境。” 你同意吗?

4

6 回答 6

38

如果我的直觉是正确的,并且您实际上想要修改环境以使生成的(分叉的)子进程 ( Runtime.getRuntime().exec()) 受益,那么请使用ProcessBuilder而不是exec(). 您可以通过ProcessBuilder实例的environment()方法构建自定义环境。

如果这不是您要达到的目标,请忽略此答案。


更新

您的三个更新的具体问题的答案如下:

  1. 我们可以修改当前进程的环境吗?
    • 不容易。取决于您是要更改进程的环境,还是要更改System.getenv()同一 JVM 中返回的值,还是两者兼而有之。
    • 正如 Greg Hewgill 指出的那样,要更改当前进程的环境,您可以setenv通过 JNI 调用或其特定于平台的等效项。您也可以使用下面第 2 点中极其复杂的方法,该方法适用于任何进程(前提是您具有权限。)但是,请注意,在大多数 JVM 中,这种更改可能永远不会反映在返回的值中System.getenv(),因为环境是通常在虚拟机启动时缓存在一个java.util.Map(或等效的)中。
    • 要更改环境的 JVM 缓存副本,当使用缓存时(请参阅System.java您将用于部署的任何 JVM 发行版中的源代码),您可以尝试破解实现(通过类加载顺序、反射检测。 ) 以 SUN 的 v1.6 JVM 为例,环境缓存由未记录的ProcessEnvironment类管理(您可以对其进行修补。)
  2. 我们可以修改父进程的环境吗?
  3. 我们可以修改子进程的环境吗?
    • 的,通过ProcessBuilder时产生进程。
    • 如果在需要更改环境时进程已经生成,则需要上面的方法 2(或一些同样复杂的方法,例如生成时的代码注入,由父进程通过例​​如套接字进行隐蔽控制。)

请注意,上述所有方法,除了涉及 的方法外ProcessBuilder,都很脆弱,容易出错,在不同程度上不可移植,并且在多线程环境中容易出现竞争条件。

于 2009-02-24T01:10:49.183 回答
12

您可以掌握java.lang.ProcessEnvironment所保留的底层地图的句柄,然后添加新内容并删除您想要的所有内容。

这适用于 java 1.8.0_144。不能保证它适用于任何其他版本的 java,但如果您确实需要在运行时更改环境,它可能是相似的。

private static Map<String,String> getModifiableEnvironment() throws Exception{
    Class pe = Class.forName("java.lang.ProcessEnvironment");
    Method getenv = pe.getDeclaredMethod("getenv");
    getenv.setAccessible(true);
    Object unmodifiableEnvironment = getenv.invoke(null);
    Class map = Class.forName("java.util.Collections$UnmodifiableMap");
    Field m = map.getDeclaredField("m");
    m.setAccessible(true);
    return (Map) m.get(unmodifiableEnvironment);
}

在您获得对地图的引用后,只需添加您想要的任何内容,您现在就可以使用常规的旧System.getenv("")调用来检索它。

我试过了,它在 MAC 中的工作在两个操作系统 java 版本 1.8_161 中都不能在 Windows 中工作

于 2017-09-26T22:43:50.010 回答
7

针对您更新的问题:

  1. 我们可以修改当前进程的环境吗?
    的,如果你使用 JNI 来调用setenv()什么的。不过,您可能不需要这样做,而且它可能不适用于所有情况。
  2. 我们可以修改父进程的环境吗?
    没有
  3. 我们可以修改子进程的环境吗?
    的,使用ProcessBuilder.
于 2009-02-24T20:50:35.277 回答
6

@Snowbuilder 的回答让我走上了正轨,但它神秘地不适用于 Oracle JDK 1.8.0_231(他使用次要版本 _144 进行了测试)。即使我能够更新底层 Map(通过System.getenv()在向 Map 添加新属性之前和之后打印出来进行验证),当使用System.getenv("property").

经过一番调查,我发现这是因为System.getenv()最终System.getenv("property")使用了不同的静态属性java.lang.ProcessEnvironment,这些属性在类static块中初始化。因此,是否将新属性添加到使用System.getenv();检索的 Map 并不重要。这些属性在 使用的其他地图中将不可用System.getenv("property")

所以我改变了其他答案的代码来处理这种情况,并来到下面的代码。请注意,这仅在您使用System.getenv("property");检索属性时才有效。如果您使用System.getenv().get("property"),那么他的答案就是您所需要的。用法如下:

@SuppressWarnings("unchecked")
private static Map<String, String> getModifiableEnvironment() throws Exception
{
    Class<?> pe = Class.forName("java.lang.ProcessEnvironment");
    Method getenv = pe.getDeclaredMethod("getenv", String.class);
    getenv.setAccessible(true);
    Field props = pe.getDeclaredField("theCaseInsensitiveEnvironment");
    props.setAccessible(true);
    return (Map<String, String>) props.get(null);
}

此方法应按如下方式使用:

getModifiableEnvironment().put("propName", "propValue");
System.getenv("propName"); // this will return "propValue"
于 2020-07-08T19:10:18.180 回答
3

我不这么认为,至少不是纯粹在 Java 中,但你为什么需要这样做呢?在 Java 中,最好通过 使用属性System.getProperties(),您可以对其进行修改。

如果你真的必须这样做,我相信你可以将 Csetenv函数包装在 JNI 调用中——事实上,如果有人已经这样做了,我不会感到惊讶。不过,我不知道代码的详细信息。

于 2009-02-24T01:05:31.973 回答
0

您可以修改当前和子进程的环境,但不能修改生成此进程的父进程的环境。

于 2009-02-24T01:47:50.437 回答