Вопрос-ответ

How do I set environment variables from Java?

Как мне установить переменные окружения из Java?

Как мне установить переменные окружения из Java? Я вижу, что могу сделать это для подпроцессов с помощью ProcessBuilder. Однако мне нужно запустить несколько подпроцессов, поэтому я бы предпочел изменить среду текущего процесса и позволить подпроцессам наследовать ее.

Есть System.getenv(String) для получения одной переменной окружения. Я также могу получить Map полный набор переменных окружения с помощью System.getenv(). Но, вызывая put() это Map выдает UnsupportedOperationException - очевидно, они означают, что среда должна быть доступна только для чтения. И нет System.setenv().

Итак, есть ли какой-либо способ установить переменные окружения в текущем запущенном процессе? Если да, то каким образом? Если нет, то в чем обоснование? (Это потому, что это Java, и поэтому я не должен делать злые непереносимые устаревшие вещи, такие как прикосновение к моей среде?) А если нет, то какие-нибудь хорошие предложения по управлению изменениями переменных окружения, которые мне нужно будет передать нескольким подпроцессам?

Переведено автоматически
Ответ 1

Для использования в сценариях, где вам нужно установить определенные значения среды для модульных тестов, вам может пригодиться следующий хак. Это изменит переменные окружения во всей JVM (поэтому убедитесь, что вы сбросили все изменения после тестирования), но не изменит вашу системную среду.

Я обнаружил, что комбинация двух грязных взломов Эдварда Кэмпбелла и anonymous работает лучше всего, поскольку один не работает под Linux, а другой не работает под Windows 7. Итак, чтобы получить мультиплатформенный злой хак, я объединил их:

protected static void setEnv(Map<String, String> newenv) throws Exception {
try {
Class<?> processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment");
Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment");
theEnvironmentField.setAccessible(true);
Map<String, String> env = (Map<String, String>) theEnvironmentField.get(null);
env.putAll(newenv);
Field theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment");
theCaseInsensitiveEnvironmentField.setAccessible(true);
Map<String, String> cienv = (Map<String, String>) theCaseInsensitiveEnvironmentField.get(null);
cienv.putAll(newenv);
} catch (NoSuchFieldException e) {
Class[] classes = Collections.class.getDeclaredClasses();
Map<String, String> env = System.getenv();
for(Class cl : classes) {
if("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
Field field = cl.getDeclaredField("m");
field.setAccessible(true);
Object obj = field.get(env);
Map<String, String> map = (Map<String, String>) obj;
map.clear();
map.putAll(newenv);
}
}
}
}

Это работает как по волшебству. Полная благодарность двум авторам этих взломов.

Ответ 2

(Это потому, что это Java, и поэтому я не должен делать злые непереносимые устаревшие вещи, такие как прикосновение к моей среде?)


Я думаю, вы попали в самую точку.

Возможным способом облегчить нагрузку было бы исключить метод

void setUpEnvironment(ProcessBuilder builder) {
Map<String, String> env = builder.environment();
// blah blah
}

и передать любые ProcessBuilders через него перед их запуском.

Кроме того, вы, вероятно, уже знаете это, но вы можете запускать более одного процесса с одним и тем жеProcessBuilder. Итак, если ваши подпроцессы одинаковы, вам не нужно выполнять эту настройку снова и снова.

Ответ 3
public static void set(Map<String, String> newenv) throws Exception {
Class[] classes = Collections.class.getDeclaredClasses();
Map<String, String> env = System.getenv();
for(Class cl : classes) {
if("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
Field field = cl.getDeclaredField("m");
field.setAccessible(true);
Object obj = field.get(env);
Map<String, String> map = (Map<String, String>) obj;
map.clear();
map.putAll(newenv);
}
}
}

Или добавить / обновить один var и удалить цикл в соответствии с предложением thejoshwolfe.

@SuppressWarnings({ "unchecked" })
public static void updateEnv(String name, String val) throws ReflectiveOperationException {
Map<String, String> env = System.getenv();
Field field = env.getClass().getDeclaredField("m");
field.setAccessible(true);
((Map<String, String>) field.get(env)).put(name, val);
}
Ответ 4

Настройка отдельных переменных окружения (на основе ответа Эдварда Кэмпбелла):

public static void setEnv(String key, String value) {
try {
Map<String, String> env = System.getenv();
Class<?> cl = env.getClass();
Field field = cl.getDeclaredField("m");
field.setAccessible(true);
Map<String, String> writableEnv = (Map<String, String>) field.get(env);
writableEnv.put(key, value);
} catch (Exception e) {
throw new IllegalStateException("Failed to set environment variable", e);
}
}

Использование:

Сначала поместите метод в любой класс, который вы хотите, например SystemUtil . Затем вызовите его статически:

SystemUtil.setEnv("SHELL", "/bin/bash");

Если вы вызовете System.getenv("SHELL") после этого, вы получите "/bin/bash" ответ.

java