environment-variables 자바 - Java로 환경 변수를 설정하려면 어떻게해야합니까?




명령 확인 (12)

이것은 @ paul-blair의 대답을 Java로 변환 한 것으로 폴 블레어가 지적한 정리와 @pdoty의 코드 안에있는 @Edward Campbell과 익명의 실수를 포함합니다.

나는이 코드가 테스트에서 얼마나 많이 사용되어야하고 매우 해킹되는지를 강조 할 수 없다. 그러나 테스트에서 환경 설정이 필요한 경우 정확하게 필요한 부분입니다.

여기에는 또한 코드가 실행되는 Windows와 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));
}

Java로 환경 변수를 설정하려면 어떻게해야합니까? ProcessBuilder 사용하여 하위 프로세스에 대해이 작업을 수행 할 수 있음을 알았습니다. 하지만 몇 가지 하위 프로세스를 시작해야합니다. 그래서 현재 프로세스의 환경을 수정하고 하위 프로세스가이를 상속하도록합니다.

단일 환경 변수를 가져 오는 System.getenv (String)가 있습니다. 또한 System.getenv ()를 사용하여 전체 환경 변수 집합에 대한 Map을 얻을 수 있습니다. 그러나 Map에서 put ()을 호출하면 UnsupportedOperationException이 throw됩니다. 이는 환경이 읽기 전용임을 의미합니다. 그리고 System.setenv ()도 없습니다.

그래서 현재 실행중인 프로세스에서 환경 변수를 설정하는 방법이 있습니까? 그렇다면 어떻게? 그렇지 않다면 그 이유는 무엇입니까? (자바인지, 따라서 내 환경을 만지기 같은 쓸모없는 이식 할 수없는 일을해서는 안되는가?) 그렇지 않다면 환경 변수를 관리하기위한 좋은 제안은 몇 가지로 먹이를 줄 필요가있다. 하위 프로세스?


(자바가 아니기 때문에 내 환경을 만지기 같은 악의적 인 이식 할 수없는 일을해서는 안되기 때문입니까?)

네가 머리에 못을 박은 것 같아.

부담을 덜어 줄 수있는 방법은 방법을 제외시키는 것입니다

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

ProcessBuilder 를 시작하기 전에 그것을 통해 모든 ProcessBuilder 전달하십시오.

또한 이미 알고 있을지 모르겠지만 동일한 ProcessBuilder 둘 이상의 프로세스를 시작할 수 있습니다. 따라서 하위 프로세스가 동일하면이 설정을 반복해서 수행 할 필요가 없습니다.


Linux 전용

단일 환경 변수 설정 (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") 를 호출 System.getenv("SHELL") "/bin/bash" 반환됩니다.


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

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

Libcore 클래스는 물론 인터페이스 OS도 공개되어 있습니다. 클래스 선언 만 빠져있어서 링커에게 보여줘야합니다. 응용 프로그램에 클래스를 추가 할 필요는 없지만 클래스가 포함 되어도 상처를주지는 않습니다.

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

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

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

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

모든 일을 할 수 있다면, 자바 자체가 나를 똑바로 재킷에 넣는 대신에 이것을 지원하는 것은 그리 어렵지 않을 것입니다.

Perl을 사용하는 친구는 환경 변수가 프로세스 전역이며 Java가 좋은 디자인을 위해 좋은 분리를 위해 노력하고 있기 때문에 이것이 다른 곳에서 제안합니다.


이 스레드를 찾은 대부분의 사람들과 마찬가지로, 나는 단위 테스트를 작성하고 테스트를 실행하기위한 올바른 조건을 설정하기 위해 환경 변수를 수정해야했습니다. 그러나, 나는 대부분의 upvoted 대답이 약간의 이슈를 가지고 있거나 매우 또는 매우 지나치게 복잡하거나 지나치게 복잡하다는 것을 발견했다. 다행히도 다른 사람들이 솔루션을 더 빨리 분류 할 수 있기를 바랍니다.

먼저, @Hubert Grzeskowiak의 솔루션이 가장 단순하다는 것을 마침내 발견했으며 나에게 도움이되었습니다. 내가 먼저 그걸로 왔으면 좋았을 텐데. 그것은 @Edward Campbell의 대답을 기반으로하지만 루프 검색을 복잡하게하지는 않습니다.

그러나, 나는 대부분의 upvotes있어 @ pushy의 솔루션으로 시작했다. @ 익명 및 @ 에드워드 캠벨의 콤보입니다. Linux와 Windows 환경 모두를 다루기 위해서는 두 가지 접근 방식이 모두 필요하다고 주장합니다. 저는 OS X에서 실행 중이며 두 작업 (한 번 익명 접근법을 가진 문제가 수정 됨)을 발견했습니다. 다른 사람들이 지적했듯이이 솔루션은 대부분의 경우 작동하지만 전부는 아닙니다.

나는 대부분의 혼란의 근원이 'theEnvironment'분야에서 작동하는 @ 익명의 해결책으로부터 비롯된 것이라고 생각한다. ProcessEnvironment 구조체의 정의를 살펴보면 'theEnvironment'는 Map <String, String>이 ​​아니라 Map <Variable, Value>입니다. 맵을 지우는 것은 정상적으로 작동하지만 putAll 작업은 map <String, String> 맵을 다시 작성합니다. Map <String, String>을 필요로하는 일반 API를 사용하여 후속 작업이 데이터 구조에서 작동 할 때 문제가 발생할 수 있습니다. 또한 개별 요소에 대한 액세스 / 제거가 문제입니다. 해결 방법은 'theUnvironment 환경'을 통해 간접적으로 '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);
}

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

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

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

위의 뻔뻔스러운 대답을 시도하고 그것은 대부분의 일을했습니다. 그러나 특정 상황에서이 예외가 표시됩니다.

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

이것은 ProcessEnvironment. 의 특정 내부 클래스 구현으로 인해 메서드가 두 번 이상 호출되었을 때 발생합니다 ProcessEnvironment. setEnv(..) 메소드가 두 번 이상 호출되면 theEnvironment 맵에서 키를 검색 할 때 키는 문자열 ( setEnv(...) 의 첫 번째 호출에 의해 문자열로 저장 setEnv(...) )이 될 수 없습니다. 지도의 제네릭 형식 인 Variable, 캐스팅됩니다 ProcessEnvironment.ProcessEnvironment.ProcessEnvironment. 의 개인 내부 클래스입니다 ProcessEnvironment.

고정 된 버전 (Scala에서)은 다음과 같습니다. 다행히도 자바로 이식하는 것은 그리 어렵지 않을 것입니다.

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()
  }
}

로그인 쉘

/etc/profile

쉘은 먼저 / etc / profile에있는 명령을 실행합니다. 루트 권한으로 작업하는 사용자는이 파일을 설정하여 bash를 실행하는 사용자에 대해 시스템 전체의 기본 특성을 설정할 수 있습니다.

.bash_profile 
.bash_login 
.profile

다음으로 셸은 ~ / .bash_profile, ~ / .bash_login 및 ~ / .profile (~ /은 홈 디렉토리의 짧은 쪽)을 찾은 다음 찾은 첫 번째 파일에서 명령을 실행합니다. 이 파일 중 하나에 명령을 넣어서 / etc / profile에 설정된 기본값을 대체 할 수 있습니다. 가상 터미널에서 실행되는 쉘은이 파일에서 명령을 실행하지 않습니다.

.bash_logout

로그 아웃하면 bash는 ~ / .bash_logout 파일에서 명령을 실행합니다. 이 파일에는 임시 파일을 제거하는 것과 같이 세션 이후 정리하는 명령이 들어 있습니다.

대화 형 비 로그인 셸

/etc/bashrc

bash가 직접 호출하지는 않았지만 ~ / .bashrc 파일은 / etc / bashrc를 호출합니다. 이 설정을 사용하면 root 권한으로 작업하는 사용자가 비 로그인 bash 쉘에 대한 시스템 전체 기본 특성을 설정할 수 있습니다.

.bashrc

대화 형 비 로그인 쉘은 ~ / .bashrc 파일에서 명령을 실행합니다. 일반적으로 .bash_profile과 같은 로그인 쉘의 시작 파일은이 파일을 실행하므로 로그인 및 비 로그인 쉘은 .bashrc에서 명령을 실행합니다.

.bashrc의 명령은 여러 번 실행될 수 있으며 하위 쉘은 내 보낸 변수를 상속하므로, .bash_profile 파일에 기존 변수를 추가하는 명령을 추가하는 것이 좋습니다.





java environment-variables