Java에서 환경 변수를 설정하는 방법


289

Java에서 환경 변수를 설정하는 방법 를 사용하여 하위 프로세스에 대해이 작업을 수행 할 수 있음을 알았습니다 ProcessBuilder. 그래도 시작할 하위 프로세스가 여러 개 있으므로 현재 프로세스 환경을 수정하고 하위 프로세스가 상속하도록합니다.

있어 System.getenv(String)단일 환경 변수를 얻기 위해. 로 Map전체 환경 변수 세트를 얻을 수도 있습니다 System.getenv(). 그러나, 호출 put()그것에 것은 Map발생 UnsupportedOperationException- 환경이 읽기 전용을 위해 분명히 그 의미. 그리고, 없습니다 System.setenv().

현재 실행중인 프로세스에서 환경 변수를 설정하는 방법이 있습니까? 그렇다면 어떻게? 그렇지 않다면 근거는 무엇입니까? (이것은 Java이기 때문에 환경을 만지는 것과 같이 악의적이며 쓸모없는 쓸데없는 일을해서는 안됩니까?) 그렇지 않다면 환경 변수를 관리하기위한 좋은 제안은 하위 프로세스?


System.getEnv ()는 보편적으로 설계되었으며 일부 환경에는 환경 변수가 없습니다.
b1nary.atr0phy

7
단위 테스트 유스 케이스에 이것을 필요로하는 사람이라면 : stackoverflow.com/questions/8168884/…
Atifm

Scala의 경우 다음을 사용하십시오. gist.github.com/vpatryshev/b1bbd15e2b759c157b58b68c58891ff4
Vlad Patryshev

답변:


88

(이것은 Java이기 때문에 환경에 닿는 것과 같은 악의적이며 쓸모없는 쓸데없는 일을해서는 안됩니까?)

나는 당신이 머리에 못을 박았다고 생각합니다.

부담을 완화하는 가능한 방법은 방법을 제외시키는 것입니다

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

ProcessBuilder시작하기 전에 s를 통과하십시오 .

또한 이미 알고있을 수도 있지만 같은로 여러 프로세스를 시작할 수 있습니다 ProcessBuilder. 따라서 하위 프로세스가 동일하면이 설정을 반복해서 수행 할 필요가 없습니다.


1
그것은 부끄러운 관리이기 때문에 악의적 인 더 이상 사용되지 않는 하위 프로세스를 실행하기 위해 다른 휴대용 언어를 사용할 수 없습니다. :)
skiphoppy

18
S.Lott, 나는 부모의 환경을 설정하려고하지 않습니다. 내 환경을 설정하려고합니다.
skiphoppy

3
프로세스를 시작하는 다른 사람의 라이브러리 (예 : Sun의 라이브러리)가 아니라면 훌륭하게 작동합니다.
설리번

24
@ b1naryatr0phy 요점을 놓쳤다. 해당 변수는 프로세스에 대해 로컬이기 때문에 아무도 환경 변수를 사용할 수 없습니다 (Windows에서 설정 한 값이 기본값 임). Java가 아닌 한 각 프로세스는 자체 변수를 자유롭게 변경할 수 있습니다.
maaartinus

9
자바의 이러한 제한은 약간의 문제이다. "자바가 이것을 원하지 않기 때문에"이외의 환경 변수를 설정하지 못하게하는 이유는 없습니다.
IanNorton

232

단위 테스트를 위해 특정 환경 값을 설정해야하는 시나리오에서 사용하려면 다음 해킹이 유용 할 수 있습니다. JVM 전체에서 환경 변수가 변경되므로 테스트 후 변경 사항을 재설정해야하지만 시스템 환경은 변경되지 않습니다.

나는 에드워드 캠벨 (Edward Campbell)의 두 가지 더러운 해킹과 익명의 조합이 가장 잘 작동한다는 것을 알았습니다. 하나는 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);
      }
    }
  }
}

이것은 매력처럼 작동합니다. 이 핵의 두 저자에게 완전한 크레딧.


1
메모리에서만 변경됩니까, 아니면 실제로 시스템의 전체 환경 변수가 변경됩니까?
Shervin Asgari

36
메모리의 환경 변수 만 변경됩니다. 테스트에 필요에 따라 환경 변수를 설정할 수 있지만 환경을 그대로 시스템에 그대로두기 때문에 테스트에 좋습니다. 사실, 나는 테스트 이외의 다른 목적으로이 코드를 사용하지 않는 것이 좋습니다. 이 코드는 악하다 ;-)
pushy

9
참고로 JVM은 시작할 때 환경 변수의 사본을 작성합니다. 그러면 JVM을 시작한 상위 프로세스의 환경 변수가 아닌 해당 사본이 편집됩니다.
14:12에

나는 이것을 안드로이드에서 시도했지만 가져 가지 않은 것 같습니다. 다른 사람이 안드로이드에서 운이 있습니까?
한스 크리스토프 스타이너

5
물론,import java.lang.reflect.Field;
뻔뻔

63
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);
        }
    }
}

또는 joshwolfe의 제안에 따라 단일 var를 추가 / 업데이트하고 루프를 제거하십시오.

@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);
  }

3
메모리에서 맵을 수정하는 것처럼 들리지만 값을 시스템에 저장합니까?
Jon Onstott 2009

1
잘 환경 변수의 메모리 맵을 변경합니다. 나는 그것이 많은 유스 케이스로 충분하다고 생각합니다. @ 에드워드-어이,이 솔루션이 처음에 어떻게 이해되었는지 상상하기가 어렵습니다!
anirvan

13
이것은 시스템의 환경 변수를 변경하지는 않지만 현재 Java 호출에서 변경합니다. 이것은 단위 테스트에 매우 유용합니다.
스튜어트 K

10
Class<?> cl = env.getClass();for 루프 대신 사용 하지 않습니까?
thejoshwolfe

1
이것은 내가 찾던 것입니다! 어떤 이유로 든 환경 변수로 불완전하게 짧은 기본 시간 초과 길이 만 수정할 수있는 타사 도구를 사용하는 일부 코드에 대한 통합 테스트를 작성했습니다.
David DeMar

21
// this is a dirty hack - but should be ok for a unittest.
private void setNewEnvironmentHack(Map<String, String> newenv) throws Exception
{
  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.clear();
  env.putAll(newenv);
  Field theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment");
  theCaseInsensitiveEnvironmentField.setAccessible(true);
  Map<String, String> cienv = (Map<String, String>) theCaseInsensitiveEnvironmentField.get(null);
  cienv.clear();
  cienv.putAll(newenv);
}

17

안드로이드에서 인터페이스는 일종의 숨겨진 API로 Libcore.os를 통해 노출됩니다.

Libcore.os.setenv("VAR", "value", bOverwrite);
Libcore.os.getenv("VAR"));

인터페이스 OS뿐만 아니라 Libcore 클래스도 공개입니다. 클래스 선언 만 누락되어 링커에 표시되어야합니다. 응용 프로그램에 클래스를 추가 할 필요는 없지만 클래스가 포함되어 있으면 아프지 않습니다.

package libcore.io;

public final class Libcore {
    private Libcore() { }

    public static Os os;
}

package libcore.io;

public interface Os {
    public String getenv(String name);
    public void setenv(String name, String value, boolean overwrite) throws ErrnoException;
}

1
Android 4.4.4 (CM11)에서 테스트 및 작업 PS 내가 한 유일한 조정은로 교체 throws ErrnoException되었습니다 throws Exception.
DavisNT

7
API 21이 Os.setEnv있습니다. developer.android.com/reference/android/system/… , java.lang.String, boolean)
Jared Burrows

1
파이의 새로운 제한으로 인해 현재 소멸 될 수 있습니다 : developer.android.com/about/versions/pie/…
TWiStErRob

13

리눅스 만

단일 환경 변수 설정 (Edward Campbell의 답변 기반) :

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"다시 돌아옵니다.


위는 윈도우 10에서 작동하지 않습니다,하지만 리눅스에서 작동합니다.
mengchengfeng

흥미 롭군 Windows에서 직접 시도하지 않았습니다. @mengchengfeng에 오류가 있습니까?
허버트 Grzeskowiak

우리는 오류 메시지가 표시되지 않았다 @HubertGrzeskowiak, 그냥 작동하지 않았다 ...
mengchengfeng

9

이것은 자바로 변환 된 @ paul-blair의 답변과 paul blair에 의해 지적 된 정리 및 @Edward Campbell으로 구성된 @pushy 코드 내부에있는 것으로 보이는 일부 실수와 익명을 포함하는 조합입니다.

나는이 코드가 테스트에 얼마나 사용되어야하는지 강조 할 수 없으며 매우 해킹 적이다. 그러나 테스트에서 환경 설정이 필요한 경우 정확히 필요합니다.

여기에는 코드가 작동하는 두 Windows 모두에서 작동 할 수있는 약간의 터치가 포함됩니다.

java version "1.8.0_92"
Java(TM) SE Runtime Environment (build 1.8.0_92-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.92-b14, mixed mode)

Centos는 물론

openjdk version "1.8.0_91"
OpenJDK Runtime Environment (build 1.8.0_91-b14)
OpenJDK 64-Bit Server VM (build 25.91-b14, mixed mode)

구현 :

/**
 * Sets an environment variable FOR THE CURRENT RUN OF THE JVM
 * Does not actually modify the system's environment variables,
 *  but rather only the copy of the variables that java has taken,
 *  and hence should only be used for testing purposes!
 * @param key The Name of the variable to set
 * @param value The value of the variable to set
 */
@SuppressWarnings("unchecked")
public static <K,V> void setenv(final String key, final String value) {
    try {
        /// we obtain the actual environment
        final Class<?> processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment");
        final Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment");
        final boolean environmentAccessibility = theEnvironmentField.isAccessible();
        theEnvironmentField.setAccessible(true);

        final Map<K,V> env = (Map<K, V>) theEnvironmentField.get(null);

        if (SystemUtils.IS_OS_WINDOWS) {
            // This is all that is needed on windows running java jdk 1.8.0_92
            if (value == null) {
                env.remove(key);
            } else {
                env.put((K) key, (V) value);
            }
        } else {
            // This is triggered to work on openjdk 1.8.0_91
            // The ProcessEnvironment$Variable is the key of the map
            final Class<K> variableClass = (Class<K>) Class.forName("java.lang.ProcessEnvironment$Variable");
            final Method convertToVariable = variableClass.getMethod("valueOf", String.class);
            final boolean conversionVariableAccessibility = convertToVariable.isAccessible();
            convertToVariable.setAccessible(true);

            // The ProcessEnvironment$Value is the value fo the map
            final Class<V> valueClass = (Class<V>) Class.forName("java.lang.ProcessEnvironment$Value");
            final Method convertToValue = valueClass.getMethod("valueOf", String.class);
            final boolean conversionValueAccessibility = convertToValue.isAccessible();
            convertToValue.setAccessible(true);

            if (value == null) {
                env.remove(convertToVariable.invoke(null, key));
            } else {
                // we place the new value inside the map after conversion so as to
                // avoid class cast exceptions when rerunning this code
                env.put((K) convertToVariable.invoke(null, key), (V) convertToValue.invoke(null, value));

                // reset accessibility to what they were
                convertToValue.setAccessible(conversionValueAccessibility);
                convertToVariable.setAccessible(conversionVariableAccessibility);
            }
        }
        // reset environment accessibility
        theEnvironmentField.setAccessible(environmentAccessibility);

        // we apply the same to the case insensitive environment
        final Field theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment");
        final boolean insensitiveAccessibility = theCaseInsensitiveEnvironmentField.isAccessible();
        theCaseInsensitiveEnvironmentField.setAccessible(true);
        // Not entirely sure if this needs to be casted to ProcessEnvironment$Variable and $Value as well
        final Map<String, String> cienv = (Map<String, String>) theCaseInsensitiveEnvironmentField.get(null);
        if (value == null) {
            // remove if null
            cienv.remove(key);
        } else {
            cienv.put(key, value);
        }
        theCaseInsensitiveEnvironmentField.setAccessible(insensitiveAccessibility);
    } catch (final ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
        throw new IllegalStateException("Failed setting environment variable <"+key+"> to <"+value+">", e);
    } catch (final NoSuchFieldException e) {
        // we could not find theEnvironment
        final Map<String, String> env = System.getenv();
        Stream.of(Collections.class.getDeclaredClasses())
                // obtain the declared classes of type $UnmodifiableMap
                .filter(c1 -> "java.util.Collections$UnmodifiableMap".equals(c1.getName()))
                .map(c1 -> {
                    try {
                        return c1.getDeclaredField("m");
                    } catch (final NoSuchFieldException e1) {
                        throw new IllegalStateException("Failed setting environment variable <"+key+"> to <"+value+"> when locating in-class memory map of environment", e1);
                    }
                })
                .forEach(field -> {
                    try {
                        final boolean fieldAccessibility = field.isAccessible();
                        field.setAccessible(true);
                        // we obtain the environment
                        final Map<String, String> map = (Map<String, String>) field.get(env);
                        if (value == null) {
                            // remove if null
                            map.remove(key);
                        } else {
                            map.put(key, value);
                        }
                        // reset accessibility
                        field.setAccessible(fieldAccessibility);
                    } catch (final ConcurrentModificationException e1) {
                        // This may happen if we keep backups of the environment before calling this method
                        // as the map that we kept as a backup may be picked up inside this block.
                        // So we simply skip this attempt and continue adjusting the other maps
                        // To avoid this one should always keep individual keys/value backups not the entire map
                        LOGGER.info("Attempted to modify source map: "+field.getDeclaringClass()+"#"+field.getName(), e1);
                    } catch (final IllegalAccessException e1) {
                        throw new IllegalStateException("Failed setting environment variable <"+key+"> to <"+value+">. Unable to access field!", e1);
                    }
                });
    }
    LOGGER.info("Set environment variable <"+key+"> to <"+value+">. Sanity Check: "+System.getenv(key));
}

7

@ pushy / @ anonymous / @ Edward Campbell의 솔루션은 Android가 실제로 Java가 아니기 때문에 Android에서 작동하지 않는 것으로 나타났습니다. 특히 Android에는 전혀 없습니다 java.lang.ProcessEnvironment. 그러나 안드로이드에서는 더 쉽다는 것이 밝혀졌습니다 setenv(). POSIX에 대한 JNI 호출 만하면됩니다 .

C / JNI에서 :

JNIEXPORT jint JNICALL Java_com_example_posixtest_Posix_setenv
  (JNIEnv* env, jclass clazz, jstring key, jstring value, jboolean overwrite)
{
    char* k = (char *) (*env)->GetStringUTFChars(env, key, NULL);
    char* v = (char *) (*env)->GetStringUTFChars(env, value, NULL);
    int err = setenv(k, v, overwrite);
    (*env)->ReleaseStringUTFChars(env, key, k);
    (*env)->ReleaseStringUTFChars(env, value, v);
    return err;
}

그리고 자바에서 :

public class Posix {

    public static native int setenv(String key, String value, boolean overwrite);

    private void runTest() {
        Posix.setenv("LD_LIBRARY_PATH", "foo", true);
    }
}

5

이 스레드를 찾은 대부분의 사람들과 마찬가지로, 몇 가지 단위 테스트를 작성했으며 테스트를 실행하기위한 올바른 조건을 설정하기 위해 환경 변수를 수정해야했습니다. 그러나 가장 많이 찬성 된 답변에 몇 가지 문제가 있거나 매우 복잡하거나 지나치게 복잡하다는 것을 알았습니다. 잘하면 이것이 다른 사람들이 솔루션을 더 빨리 분류하는 데 도움이되기를 바랍니다.

먼저 @Hubert Grzeskowiak의 솔루션이 가장 간단하다는 것을 알았습니다. 나는 그 중 하나에 먼저 왔으면 좋겠다. @Edward Campbell의 답변을 기반으로하지만 루프 검색이 복잡하지 않습니다.

그러나 @pushy의 솔루션으로 시작했는데 가장 많은 찬사를 받았습니다. @anonymous와 @Edward Campbell의 조합입니다. @pushy는 Linux와 Windows 환경을 모두 다루기 위해 두 가지 접근 방식이 모두 필요하다고 주장한다 OS X에서 실행 중이며 두 가지 모두 작동합니다 (@anonymous 접근 방식의 문제가 해결 된 경우). 다른 사람들이 지적 했듯이이 솔루션은 대부분 작동하지만 전부는 아닙니다.

혼란의 원인은 '환경'필드에서 작동하는 @anonymous의 솔루션에서 비롯된 것 같습니다. ProcessEnvironment 구조 의 정의를 보면 'TheEnvironment'는 Map <String, String>이 ​​아니라 Map <Variable, Value>입니다. 맵을 지우면 정상적으로 작동하지만 putAll 조작은 Map <String, String> 맵을 다시 빌드하므로 Map <Variable, Value>를 예상하는 일반 API를 사용하여 데이터 구조에서 후속 조작이 조작 될 때 잠재적으로 문제점이 발생합니다. 또한 개별 요소에 액세스 / 제거하는 것이 문제입니다. 해결책은 '수정 불가능한 환경'을 통해 '환경'에 간접적으로 액세스하는 것입니다. 그러나 이것은 유형이기 때문에 UnmodifiableMapUnmodifiableMap 유형의 개인 변수 '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);
}

고마워, 그것은 나의 유스 케이스 였고 mac os x에서도 마찬가지였습니다.
Rafael Gonçalves

이것을 좋아해서 Groovy를 위해 약간 더 간단한 버전을 만들었습니다. 아래를 참조하십시오.
mike rodent

4

온라인으로 돌아 다니면 JNI 로이 작업을 수행 할 수있는 것처럼 보입니다. 그런 다음 C에서 putenv ()를 호출해야하며 아마도 Windows와 UNIX에서 모두 작동하는 방식으로 수행해야합니다.

모든 것이 가능하다면, 자바가 저를 똑 바른 재킷에 넣는 대신 이것을 지원하기가 너무 어렵지 않을 것입니다.

다른 곳에서 Perl을 사용하는 친구는 환경 변수가 전역 적으로 처리되고 Java가 우수한 디자인을위한 우수한 격리를 위해 노력하고 있기 때문이라고 말합니다.


예, C 코드에서 프로세스 환경을 설정할 수 있습니다. 그러나 나는 자바에서 일하는 것을 믿지 않을 것입니다. 시작 중에 JVM이 환경을 Java 문자열 오브젝트로 복사 할 가능성이 높으므로 변경 사항이 향후 JVM 조작에 사용되지 않습니다.
Darron

경고 주셔서 감사합니다, 대런 아마 당신이 옳을 가능성이 높습니다.
skiphoppy

2
@Darron이 작업을 수행하려는 많은 이유는 JVM이 환경을 생각하는 것과 전혀 관련이 없습니다. ( LD_LIBRARY_PATH호출하기 전에 설정을 고려하십시오 Runtime.loadLibrary(); dlopen()호출하는 호출은 Java의 아이디어가 아니라 실제 환경을 봅니다 ).
Charles Duffy

이것은 네이티브 라이브러리 (내 경우에는 대부분)에서 시작한 하위 프로세스에서 작동하지만 불행히도 Java의 Process 또는 ProcessBuilder 클래스에서 시작된 하위 프로세스에서는 작동하지 않습니다.
Dan

4

위의 pushy의 답변을 시험해 보았으며 대부분 효과가있었습니다. 그러나 특정 상황 에서이 예외가 표시됩니다.

java.lang.String cannot be cast to java.lang.ProcessEnvironment$Variable

밖이 회전은 특정 내부 클래스의 구현 때문에, 메소드가 한 번 이상 호출 될 때 일어날 ProcessEnvironment.경우 setEnv(..)키가에서 검색하는 방법은 두 번 이상이라고, theEnvironment지도, 그들에 넣어 된 문자열은 (지금 의 첫 번째 호출하여 문자열로 setEnv(...)) 및지도의 제네릭 형식에 캐스트 할 수없는 Variable,의 개인 내부 클래스입니다ProcessEnvironment.

고정 버전 (스칼라)은 다음과 같습니다. 바라건대 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()
  }
}

JavaClass는 어디에 정의되어 있습니까?
Mike Slinn

1
아마 import java.lang.{Class => JavaClass}.
랜달 휘트먼

1
java.lang.ProcessEnvironment의 구현은 동일한 빌드에서도 플랫폼마다 다릅니다. 예를 들어, Windows 구현에는 java.lang.ProcessEnvironment $ Variable 클래스가 없지만이 클래스는 Linux 용 클래스에 존재합니다. 쉽게 확인할 수 있습니다. Linux 용 tar.gz JDK 배포판을 다운로드하고 src.zip에서 소스를 추출한 다음 Windows 용 배포판의 동일한 파일과 비교하십시오. JDK 1.8.0_181에서는 완전히 다릅니다. Java 10에서 확인하지 않았지만 동일한 그림이 있으면 놀라지 않을 것입니다.
Alex Konshin

1

이것은 @pushy의 사악한 답변 의 Kotlin 사악한 버전입니다 =)

@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.getDeclaredClasses()
        val env = System.getenv()
        for (cl in classes) {
            if ("java.util.Collections\$UnmodifiableMap" == cl.getName()) {
                val field = cl.getDeclaredField("m")
                field.setAccessible(true)
                val obj = field.get(env)
                val map = obj as MutableMap<String, String>
                map.clear()
                map.putAll(newenv)
            }
        }
    }

적어도 macOS Mojave에서 작동합니다.


0

SpringBoot로 작업하는 경우 다음 특성에서 환경 변수를 지정하여 추가 할 수 있습니다.

was.app.config.properties.toSystemProperties

1
좀 설명해 주시겠습니까?
Faraz

0

@pushy의 답변을 기반으로 한 변형 은 Windows에서 작동합니다.

def set_env(newenv):
    from java.lang import Class
    process_environment = Class.forName("java.lang.ProcessEnvironment")
    environment_field =  process_environment.getDeclaredField("theEnvironment")
    environment_field.setAccessible(True)
    env = environment_field.get(None)
    env.putAll(newenv)
    invariant_environment_field = process_environment.getDeclaredField("theCaseInsensitiveEnvironment");
    invariant_environment_field.setAccessible(True)
    invevn = invariant_environment_field.get(None)
    invevn.putAll(newenv)

용법:

old_environ = dict(os.environ)
old_environ['EPM_ORACLE_HOME'] = r"E:\Oracle\Middleware\EPMSystem11R1"
set_env(old_environ)

0

팀 라이언의 대답은 나를 위해 일했지만 ... 나는 Groovy (예 : Spock 컨텍스트)와 simplissimo를 원했습니다.

import java.lang.reflect.Field

def getModifiableEnvironmentMap() {
    def unmodifiableEnv = System.getenv()
    Class cl = unmodifiableEnv.getClass()
    Field field = cl.getDeclaredField("m")
    field.accessible = true
    field.get(unmodifiableEnv)
}

def clearEnvironmentVars( def keys ) {
    def savedVals = [:]
    keys.each{ key ->
        String val = modifiableEnvironmentMap.remove(key)
        // thinking about it, I'm not sure why we need this test for null
        // but haven't yet done any experiments
        if( val != null ) {
            savedVals.put( key, val )
        }
    }
    savedVals
}

def setEnvironmentVars(Map varMap) {
    modifiableEnvironmentMap.putAll(varMap)
}

// pretend existing Env Var doesn't exist
def PATHVal1 = System.env.PATH
println "PATH val1 |$PATHVal1|"
String[] keys = ["PATH", "key2", "key3"]
def savedVars = clearEnvironmentVars(keys)
def PATHVal2 = System.env.PATH
println "PATH val2 |$PATHVal2|"

// return to reality
setEnvironmentVars(savedVars)
def PATHVal3 = System.env.PATH
println "PATH val3 |$PATHVal3|"
println "System.env |$System.env|"

// pretend a non-existent Env Var exists
setEnvironmentVars( [ 'key4' : 'key4Val' ])
println "key4 val |$System.env.key4|"

0

Kotlin의 버전 인이 알고리즘에서는 환경에서 변수를 설정하고 가져올 수있는 데코레이터를 만들었습니다.

import java.util.Collections
import kotlin.reflect.KProperty

class EnvironmentDelegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return System.getenv(property.name) ?: "-"
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        val key = property.name

        val classes: Array<Class<*>> = Collections::class.java.declaredClasses
        val env = System.getenv()

        val cl = classes.first { "java.util.Collections\$UnmodifiableMap" == it.name }

        val field = cl.getDeclaredField("m")
        field.isAccessible = true
        val obj = field[env]
        val map = obj as MutableMap<String, String>
        map.putAll(mapOf(key to value))
    }
}

class KnownProperties {
    var JAVA_HOME: String by EnvironmentDelegate()
    var sample: String by EnvironmentDelegate()
}

fun main() {
    val knowProps = KnownProperties()
    knowProps.sample = "2"

    println("Java Home: ${knowProps.JAVA_HOME}")
    println("Sample: ${knowProps.sample}")
}

-1

내가 최근 Edward의 답변을 기반으로 한 Kotlin 구현 :

fun setEnv(newEnv: Map<String, String>) {
    val unmodifiableMapClass = Collections.unmodifiableMap<Any, Any>(mapOf()).javaClass
    with(unmodifiableMapClass.getDeclaredField("m")) {
        isAccessible = true
        @Suppress("UNCHECKED_CAST")
        get(System.getenv()) as MutableMap<String, String>
    }.apply {
        clear()
        putAll(newEnv)
    }
}

-12

-D를 사용하여 초기 Java 프로세스에 매개 변수를 전달할 수 있습니다.

java -cp <classpath> -Dkey1=value -Dkey2=value ...

실행시 값을 알 수 없습니다. 이들은 사용자가 제공 / 선택할 때 프로그램 실행 중에 알려지게됩니다. 환경 변수가 아닌 시스템 속성 만 설정합니다.
skiphoppy

그런 다음 하위 프로세스를 호출하는 일반적인 방법 (argss [] 매개 변수를 통해 main 메소드로)을 찾고 싶을 것입니다.
matt b

매트 b, 일반적인 방법은 원래 질문에서 언급했듯이 ProcessBuilder를 사용하는 것입니다. :)
skiphoppy

7
-D 매개 변수는를 통해 사용할 수 System.getProperty있으며와 동일하지 않습니다 System.getenv. 게다가이 System클래스는 다음 속성을 사용하여 이러한 속성을 정적으로 설정할 수 있습니다.setProperty
anirvan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.