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

回答:


88

(これはJavaであるため、環境に触れるなど、邪魔で移植できない時代遅れのことをすべきではありませんか?)

頭に釘を打ったと思います。

負担を軽減するための可能な方法は、メソッドを除外することです

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

ProcessBuilderそれらを開始する前に、それを通過します。

また、あなたはおそらくこれをすでに知っていますが、同じで複数のプロセスを開始できますProcessBuilder。したがって、サブプロセスが同じである場合、この設定を何度も行う必要はありません。


1
そのため、この邪悪で時代遅れのサブプロセスのセットを実行するために別のポータブル言語を使用することはできません。:)
skiphoppy

18
S.Lott、私は親の環境を整えるつもりはありません。自分の環境を整えたいです。
skiphoppy 2008年

3
プロセスを起動しているのが他の誰かのライブラリ(Sunのライブラリなど)でない限り、これはうまく機能します。
サリバン

24
@ b1naryatr0phyポイントを逃しました。環境変数はプロセスに対してローカルであるため、だれも環境変数を操作できません(Windowsで設定したものがデフォルト値です)。各プロセスは、Javaでない限り、独自の変数を自由に変更できます。
maaartinus 2012年

9
このjavaの制限は、ちょっとした警戒心です。「Javaでこれを実行したくないため」以外に、Javaが環境変数を設定できない理由はありません。
IanNorton 2017

232

単体テストに特定の環境値を設定する必要があるシナリオで使用するには、次のハックが役立つ場合があります。これは、JVM全体の環境変数を変更します(テスト後に変更をリセットするようにしてください)が、システム環境は変更されません。

Edward Campbellとanonymousによる2つのダーティハックの組み合わせが最も効果的であることがわかりました。1つはLinuxで機能せず、もう1つは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人の作者の完全な功績。


1
これはメモリ内でのみ変更されますか、それともシステムの環境変数全体を実際に変更しますか?
Shervin Asgari

36
これにより、メモリ内の環境変数のみが変更されます。これはテストに適しています。テストに必要に応じて環境変数を設定できますが、システム内のenvはそのままにしておきます。実際、私はこのコードをテスト以外の目的で使用しないように強くお勧めします。このコードは悪です;-)
押し付けがましい

9
参考までに、JVMは起動時に環境変数のコピーを作成します。これにより、JVMを起動した親プロセスの環境変数ではなく、そのコピーが編集されます。
2012

私はこれをAndroidで試しましたが、うまくいきませんでした。他の誰かがAndroidで運がありますか?
Hans-Christoph Steiner

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の提案に従って、単一の変数を追加/更新し、ループを削除します。

@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
まあ、それは環境変数のメモリマップを変更します。多くのユースケースでそれで十分だと思います。@Edward-まあ、このソリューションが最初にどのように考え出されたのか想像するのは難しいです!
anirvan 2010

13
これはシステムの環境変数を変更しませんが、Javaの現在の呼び出しでそれらを変更します。これは単体テストに非常に役立ちます。
スチュアートK

10
Class<?> cl = env.getClass();forループの代わりに使用しないのはなぜですか?
thejoshwolfe

1
これはまさに私が探していたものです!私は、なんらかの理由で、環境変数を使用して不合理に短いデフォルトのタイムアウト時間を変更できるだけのサードパーティツールを使用する一部のコードの統合テストを作成しています。
David DeMar 2015

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

Androidでは、インターフェイスは一種の隠しAPIとしてLibcore.osを介して公開されます。

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

1
テスト済みで、Android 4.4.4(CM11)で動作しています。PS私が行った唯一の調整は、throws ErrnoExceptionとの交換throws Exceptionでした。
DavisNT 2014

7
API 21、Os.setEnv今。developer.android.com/reference/android/system/...、java.lang.Stringで、ブール値)
ジャレッド・バロウズ

1
潜在的に故人となった今、パイの新しい制限付き:developer.android.com/about/versions/pie/...
TWiStErRob

13

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")この後で電話すると、"/bin/bash"戻ってきます。


上記窓10では動作しませんが、ますのlinuxで動作します。
mengchengfeng 2017

面白い。Windowsでは自分で試したわけではありません。@mengchengfeng、エラーが発生しますか?
Hubert Grzeskowiak 2017

@HubertGrzeskowiakエラーメッセージは表示されませんでしたが、機能しませんでした...
mengchengfeng

9

これは、Javaに変換された@ 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

Androidは実際にはJavaではないため、@ pushy / @ anonymous / @ Edward CampbellのソリューションはAndroidでは機能しないことがわかりました。具体的には、Androidにはまったくありませんjava.lang.ProcessEnvironment。しかし、Androidの方が簡単であることがわかりました。POSIXへのJNI呼び出しを行うだけです。setenv()

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

そしてJavaでは:

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アプローチの問題が修正されると)。他の人が指摘しているように、このソリューションはほとんどの場合機能しますが、すべてではありません。

ほとんどの混乱の原因は、 'theEnvironment'フィールドで動作する@anonymousのソリューションにあると思います。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);
}

おかげで、それはまさに私のユースケースであり、Mac OS Xでも同様でした。
ラファエル・ゴンサルベス

これがとても好きだったので、Groovyのやや単純なバージョンを試してみました。
マイクげっ歯類

4

オンラインでぶらぶらしているようですが、JNIでこれを実行できる可能性があるようです。次に、Cからputenv()を呼び出す必要があり、WindowsとUNIXの両方で機能する方法で(おそらく)呼び出す必要があります。

それがすべてできれば、私をストレートジャケットにする代わりに、Java自体がこれをサポートするのはそれほど難しくありません。

他の場所でPerlを話す友人は、これは環境変数がプロセスグローバルであり、Javaが優れた設計のために優れた分離を目指しているためであると示唆しています。


はい、Cコードからプロセス環境を設定できます。しかし、Javaでの作業は当てになりません。起動時にJVMが環境を​​Java Stringオブジェクトにコピーする可能性が高いため、変更は今後のJVM操作に使用されません。
ダロン

警告をありがとう、ダロン。おそらく、あなたが正しい可能性は十分にあります。
skiphoppy 2008年

2
@Darronこれを実行したい理由の多くは、JVMが環境と考えるものとは何の関係もありません。(をLD_LIBRARY_PATH呼び出す前に設定を考えてくださいRuntime.loadLibrary()dlopen()呼び出される呼び出しは、Javaの同じ概念ではなく、実際の環境を調べます)。
Charles Duffy

これは、ネイティブライブラリによって開始されたサブプロセス(私の場合はほとんど)で機能しますが、JavaのProcessクラスまたはProcessBuilderクラスによって開始されたサブプロセスでは機能しません。
Danの

4

上記の強引な答えを試してみましたが、ほとんどの部分でうまくいきました。ただし、特定の状況では、次の例外が発生します。

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

JavaClassはどこに定義されていますか?
Mike Slinn

1
たぶんimport java.lang.{Class => JavaClass}
Randall Whitman

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
少し説明してもらえますか?
ファラス

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

エドワードの答えに基づいて私が最近行った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 2008年

次に、その場合、サブプロセスを呼び出す通常の方法を(メインメソッドへのargs []パラメーターを介して)見つける必要があります。
matt b

私の元の質問で述べたように、マットb、通常の方法はProcessBuilderを経由します。:)
skiphoppy 2008年

7
-Dパラメータはから利用でき、System.getPropertyとは異なりますSystem.getenv。さらに、Systemクラスはこれらのプロパティを静的に設定することもできますsetProperty
anirvan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.