如何从Java设置环境变量?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何从Java设置环境变量?相关的知识,希望对你有一定的参考价值。
如何从Java设置环境变量?我看到我可以使用ProcessBuilder
为子进程执行此操作。我有几个子进程要启动,所以我宁愿修改当前进程的环境,让子进程继承它。
有一个System.getenv(String)
用于获取单个环境变量。我还可以使用Map
获取完整环境变量集的System.getenv()
。但是,在put()
上调用Map
会抛出一个UnsupportedOperationException
- 显然它们意味着环境只能被读取。并且,没有System.setenv()
。
那么,有没有办法在当前运行的进程中设置环境变量?如果是这样,怎么样?如果没有,理由是什么? (这是因为这是Java,因此我不应该做一些邪恶的非便携式过时的事情,比如触摸我的环境吗?)如果没有,那么管理环境变量的任何好建议都会改变我需要提供给几个子进程?
(这是因为这是Java,因此我不应该做一些邪恶的非便携式过时的东西,比如触摸我的环境吗?)
我觉得你已经敲了敲头。
减轻负担的一种可能方法是分解出一种方法
void setUpEnvironment(ProcessBuilder builder) {
Map<String, String> env = builder.environment();
// blah blah
}
并在启动之前通过任何ProcessBuilder
s。
此外,您可能已经知道这一点,但您可以使用相同的ProcessBuilder
启动多个进程。因此,如果您的子流程相同,则无需反复进行此设置。
尝试上面的pushy的答案,它在大多数情况下都有效。但是,在某些情况下,我会看到这个例外:
java.lang.String cannot be cast to java.lang.ProcessEnvironment$Variable
由于ProcessEnvironment.
的某些内部类的实现,如果setEnv(..)
方法被多次调用,当从theEnvironment
地图中检索到密钥时,它们现在是字符串(具有通过setEnv(...)
的第一次调用被放入字符串中,并且无法强制转换为地图的泛型类型,Variable,
是ProcessEnvironment.
的私有内部类
下面是一个固定版本(在Scala中)。希望将其延伸到Java中并不困难。
def setEnv(newenv: java.util.Map[String, String]): Unit = {
try {
val processEnvironmentClass = JavaClass.forName("java.lang.ProcessEnvironment")
val theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment")
theEnvironmentField.setAccessible(true)
val variableClass = JavaClass.forName("java.lang.ProcessEnvironment$Variable")
val convertToVariable = variableClass.getMethod("valueOf", classOf[java.lang.String])
convertToVariable.setAccessible(true)
val valueClass = JavaClass.forName("java.lang.ProcessEnvironment$Value")
val convertToValue = valueClass.getMethod("valueOf", classOf[java.lang.String])
convertToValue.setAccessible(true)
val sampleVariable = convertToVariable.invoke(null, "")
val sampleValue = convertToValue.invoke(null, "")
val env = theEnvironmentField.get(null).asInstanceOf[java.util.Map[sampleVariable.type, sampleValue.type]]
newenv.foreach { case (k, v) => {
val variable = convertToVariable.invoke(null, k).asInstanceOf[sampleVariable.type]
val value = convertToValue.invoke(null, v).asInstanceOf[sampleValue.type]
env.put(variable, value)
}
}
val theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment")
theCaseInsensitiveEnvironmentField.setAccessible(true)
val cienv = theCaseInsensitiveEnvironmentField.get(null).asInstanceOf[java.util.Map[String, String]]
cienv.putAll(newenv);
}
catch {
case e : NoSuchFieldException => {
try {
val classes = classOf[java.util.Collections].getDeclaredClasses
val env = System.getenv()
classes foreach (cl => {
if("java.util.Collections$UnmodifiableMap" == cl.getName) {
val field = cl.getDeclaredField("m")
field.setAccessible(true)
val map = field.get(env).asInstanceOf[java.util.Map[String, String]]
// map.clear() // Not sure why this was in the code. It means we need to set all required environment variables.
map.putAll(newenv)
}
})
} catch {
case e2: Exception => e2.printStackTrace()
}
}
case e1: Exception => e1.printStackTrace()
}
}
像大多数找到这个线程的人一样,我正在编写一些单元测试,需要修改环境变量来设置测试运行的正确条件。但是,我发现最受欢迎的答案有一些问题和/或非常神秘或过于复杂。希望这有助于其他人更快地解决问题。
首先,我终于发现@Hubert Grzeskowiak的解决方案是最简单的,它对我有用。我希望我能先来到那个。它基于@Edward Campbell的答案,但没有复杂的循环搜索。
但是,我开始使用@ pushy的解决方案,它得到了最多的赞成。它是@anonymous和@Edward Campbell的组合。 @pushy声称需要两种方法来涵盖Linux和Windows环境。我在OS X下运行,发现两者都有效(一旦@anonymous方法的问题得到修复)。正如其他人所指出的那样,这种解决方案大部分时间都有效,但不是全部。
我认为大多数混乱的根源来自@ anonymous在'theEnvironment'领域运行的解决方案。看一下ProcessEnvironment结构的定义,'theEnvironment'不是Map <String,String>,而是Map <Variable,Value>。清除地图工作正常,但putAll操作会重建Map <String,String>,这可能会导致后续操作使用期望Map <Variable,Value>的常规API对数据结构进行操作时出现问题。此外,访问/删除单个元素是一个问题。解决方案是通过'theUnmodifiableEnvironment'间接访问'theEnvironment'。但由于这是一个类型UnmodifiableMap,访问必须通过UnmodifiableMap类型的私有变量'm'完成。请参阅下面的代码中的getModifiableEnvironmentMap2。
在我的情况下,我需要为我的测试删除一些环境变量(其他变量应保持不变)。然后我想在测试后将环境变量恢复到先前的状态。下面的例程可以直截了当地做。我在OS X上测试了两个版本的getModifiableEnvironmentMap,两者都是等效的。虽然基于此主题中的注释,但根据环境的不同,可能是比另一个更好的选择。
注意:我没有包含对'theCaseInsensitiveEnvironmentField'的访问权限,因为这似乎是Windows特定的,我无法测试它,但添加它应该是直截了当的。
private Map<String, String> getModifiableEnvironmentMap() {
try {
Map<String,String> unmodifiableEnv = System.getenv();
Class<?> cl = unmodifiableEnv.getClass();
Field field = cl.getDeclaredField("m");
field.setAccessible(true);
Map<String,String> modifiableEnv = (Map<String,String>) field.get(unmodifiableEnv);
return modifiableEnv;
} catch(Exception e) {
throw new RuntimeException("Unable to access writable environment variable map.");
}
}
private Map<String, String> getModifiableEnvironmentMap2() {
try {
Class<?> processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment");
Field theUnmodifiableEnvironmentField = processEnvironmentClass.getDeclaredField("theUnmodifiableEnvironment");
theUnmodifiableEnvironmentField.setAccessible(true);
Map<String,String> theUnmodifiableEnvironment = (Map<String,String>)theUnmodifiableEnvironmentField.get(null);
Class<?> theUnmodifiableEnvironmentClass = theUnmodifiableEnvironment.getClass();
Field theModifiableEnvField = theUnmodifiableEnvironmentClass.getDeclaredField("m");
theModifiableEnvField.setAccessible(true);
Map<String,String> modifiableEnv = (Map<String,String>) theModifiableEnvField.get(theUnmodifiableEnvironment);
return modifiableEnv;
} catch(Exception e) {
throw new RuntimeException("Unable to access writable environment variable map.");
}
}
private Map<String, String> clearEnvironmentVars(String[] keys) {
Map<String,String> modifiableEnv = getModifiableEnvironmentMap();
HashMap<String, String> savedVals = new HashMap<String, String>();
for(String k : keys) {
String val = modifiableEnv.remove(k);
if (val != null) { savedVals.put(k, val); }
}
return savedVals;
}
private void setEnvironmentVars(Map<String, String> varMap) {
getModifiableEnvironmentMap().putAll(varMap);
}
@Test
public void myTest() {
String[] keys = { "key1", "key2", "key3" };
Map<String, String> savedVars = clearEnvironmentVars(keys);
// do test
setEnvironmentVars(savedVars);
}
这是Kotlin邪恶版的@ pushy的邪恶answer =)
@Suppress("UNCHECKED_CAST")
@Throws(Exception::class)
fun setEnv(newenv: Map<String, String>) {
try {
val processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment")
val theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment")
theEnvironmentField.isAccessible = true
val env = theEnvironmentField.get(null) as MutableMap<String, String>
env.putAll(newenv)
val theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment")
theCaseInsensitiveEnvironmentField.isAccessible = true
val cienv = theCaseInsensitiveEnvironmentField.get(null) as MutableMap<String, String>
cienv.putAll(newenv)
} catch (e: NoSuchFieldException) {
val classes = Collections::class.以上是关于如何从Java设置环境变量?的主要内容,如果未能解决你的问题,请参考以下文章