Java'dan ortam değişkenlerini nasıl ayarlarım?


289

Java'dan ortam değişkenlerini nasıl ayarlarım? Bunu kullanarak alt işlemler için yapabileceğimi görüyorum ProcessBuilder. Başlamak için birkaç alt işlemim var, bu yüzden geçerli sürecin ortamını değiştirmeyi ve alt işlemlerin miras almasını tercih ederim.

Bir var System.getenv(String)bir tek ortam değişkeni almak için. Ayrıca Maportam değişkenleri ile komple bir set alabilirsiniz System.getenv(). Ama, çağıran put()bu konuda Mapbir atar UnsupportedOperationException- çevre sadece okunacak için görünüşe göre yani. Ve, hayır System.setenv().

Peki, şu anda çalışan işlemde ortam değişkenlerini ayarlamanın bir yolu var mı? Öyleyse nasıl? Değilse, gerekçesi nedir? (Bu Java olduğu için ve bu nedenle çevreme dokunmak gibi kötü taşınabilir olmayan eski şeyler yapmamalıyım mı?) subprocesses?


System.getEnv () evrensel olarak tasarlanmıştır, bazı ortamlarda ortam değişkenleri bile yoktur.
b1nary.atr0phy

7
Birim testi için buna ihtiyaç duyan herkes için kullanım örneği: stackoverflow.com/questions/8168884/…
Atifm

Yanıtlar:


88

(Bu Java olduğu için mi ve bu nedenle çevreme dokunmak gibi kötü taşınabilir eski şeyler yapmamalıyım mı?)

Bence kafasına çiviyi vurdun.

Yükü hafifletmenin olası bir yolu, bir yöntemi ortadan kaldırmak olacaktır

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

ve ProcessBuilderbaşlamadan önce herhangi bir s geçirin.

Ayrıca, muhtemelen bunu zaten biliyorsunuzdur, ancak aynı yöntemle birden fazla işleme başlayabilirsiniz ProcessBuilder. Dolayısıyla, alt işlemleriniz aynı ise, bu kurulumu tekrar tekrar yapmanız gerekmez.


1
Bu utanç yönetimi, bu kötü, eski alt süreçleri çalıştırmak için farklı bir taşınabilir dil kullanmama izin vermiyor. :)
skiphoppy

18
S.Lott, bir ebeveynin çevresini kurmak istemiyorum. Kendi ortamımı kurmak istiyorum.
skiphoppy

3
Süreci başlatan başka birinin kütüphanesi (örneğin Sun'ın) olmadığı sürece bu harika çalışıyor.
sullivan-

24
@ b1naryatr0phy Bu noktayı kaçırdınız. Bu değişkenler bir işlem için yerel olduğu için hiç kimse ortam değişkenlerinizle oynayamaz (Windows'ta ayarladığınız varsayılan değerlerdir). Her işlem kendi değişkenlerini değiştirmekte özgürdür ... Java olmadan.
maaartinus

9
Java'nın bu sınırlaması bir polis memurudur. Java'nın "jav'ın bunu yapmasını istemiyoruz" dışında env değişkenlerini ayarlamanıza izin vermemesinin bir nedeni yoktur.
IanNorton

232

Birim testleri için belirli ortam değerlerini ayarlamanız gereken senaryolarda kullanmak için aşağıdaki hack'i yararlı bulabilirsiniz. JVM boyunca ortam değişkenlerini değiştirir (bu nedenle testten sonra değişiklikleri sıfırladığınızdan emin olun), ancak sistem ortamınızı değiştirmez.

Edward Campbell ve kirli anonim iki kirli kesmek bir arada en iyi çalışır, biri linux altında çalışmıyor, biri Windows 7 altında çalışmaz. Yani çok platformlu kötü kesmek almak için onları birleştirdi:

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

Bu bir cazibe gibi çalışır. Bu hacklerin iki yazarına tam kredi.


1
Bu sadece bellekte mi değişecek yoksa sistemdeki tüm ortam değişkenini mi değiştirecek?
Shervin Asgari

36
Bu yalnızca bellekteki ortam değişkenini değiştirir. Bu, test için iyidir, çünkü testiniz için ortam değişkenini gerektiği gibi ayarlayabilirsiniz, ancak env'leri olduğu gibi sistemde bırakabilirsiniz. Aslında, kimsenin bu kodu test etmekten başka bir amaçla kullanmasını şiddetle tavsiye etmem. Bu kod kötü ;-)
saldırgan

9
Bir FYI olarak JVM, başladığında ortam değişkenlerinin bir kopyasını oluşturur. Bu, JVM'yi başlatan üst işlemin ortam değişkenlerini değil, bu kopyayı düzenler.
2012'de

Bunu Android'de denedim ve sürmedi. Android'de şansı olan var mı?
Hans-Christoph Steiner

5
Tabii,import java.lang.reflect.Field;
saldırgan

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

Ya da tek bir var eklemek / güncellemek ve joshwolfe'nin önerisine göre döngüyü kaldırmak için.

@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
Bu, haritayı bellekte değiştirecek gibi görünüyor, ancak değeri sisteme kaydedecek mi?
Jon Onstott

1
çevre değişkenlerinin hafıza haritasını değiştirir. sanırım bu birçok kullanım durumunda yeterli. @Edward - tanrım, bu çözümün nasıl çözüldüğünü hayal etmek zor!
anirvan

13
Bu, sistemdeki ortam değişkenlerini değiştirmez, ancak geçerli Java çağrısında bunları değiştirir. Bu birim testi için çok kullanışlıdır.
Stuart K

10
neden Class<?> cl = env.getClass();döngü yerine bunu kullanmıyorsunuz ?
thejoshwolfe

1
Tam da aradığım şey bu! Bazı nedenlerle, sadece saçma kısa varsayılan zaman aşımı uzunluğunu bir çevresel değişkenle değiştirmenize izin veren üçüncü taraf bir araç kullanan bazı kodlar için entegrasyon testleri yazıyorum.
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

Android'de arayüz bir tür gizli API olarak Libcore.os aracılığıyla ortaya çıkar.

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

Libcore sınıfı ve arabirim işletim sistemi herkese açıktır. Yalnızca sınıf bildirimi eksiktir ve bağlayıcıya gösterilmesi gerekir. Sınıfları uygulamaya eklemenize gerek yoktur, ancak dahil edilirse de zarar vermez.

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) üzerinde test edilmiş ve çalışıyor. PS yaptığım sadece ayar yerine geçecekti throws ErrnoExceptionile throws Exception.
DavisNT

7
API 21, Os.setEnvşimdi var. developer.android.com/reference/android/system/… , java.lang.String, boolean)
Jared Burrows


13

Yalnızca Linux

Tek ortam değişkenlerini ayarlama (Edward Campbell'ın cevabına göre):

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

Kullanımı:

İlk olarak, yöntemi istediğiniz herhangi bir sınıfa koyun, örneğin SystemUtil. Ardından statik olarak arayın:

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

Bundan System.getenv("SHELL")sonra ararsanız , "/bin/bash"geri döneceksiniz.


Yukarıdaki pencerelerden 10 çalışmaz, ama olacak linux çalışır.
mengchengfeng

İlginç. Windows'da kendim denemedim. Bir hata mı alıyorsunuz @mengchengfeng?
Hubert Grzeskowiak

@HubertGrzeskowiak Herhangi bir hata mesajı görmedik, işe yaramadı ...
mengchengfeng

9

Bu, @ paul-blair'in Java'ya dönüştürülen yanıtının, paul blair tarafından işaret edilen bazı temizlemeleri ve @Pushy'nin @Edward Campbell ve anonimden oluşan kodunun içinde olduğu anlaşılan bazı hataları içeren bir birleşimidir.

Bu kodun SADECE testte ne kadar kullanılması gerektiğini ve son derece hacky olduğunu vurgulayamıyorum. Ancak testlerde ortam kurulumuna ihtiyaç duyduğunuz durumlarda tam da ihtiyacım olan şey budur.

Bu, kodun her iki Windows üzerinde de çalışmasına izin veren bazı küçük dokunuşları da içeriyor

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)

yanı sıra Centos çalışıyor

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)

Hayata geçirme:

/**
 * 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'ın çözümü Android'de işe yaramaz, çünkü Android gerçekten Java değildir. Özellikle, Android hiç yoktur java.lang.ProcessEnvironment. Ancak Android'de daha kolay olduğu ortaya çıktı, sadece POSIX için bir JNI çağrısı yapmanız gerekiyor setenv():

C / JNI dilinde:

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

Ve Java'da:

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

Bu iş parçacığını bulan çoğu insan gibi, bazı birim testleri yazıyordum ve testin çalışması için doğru koşulları ayarlamak üzere ortam değişkenlerini değiştirmem gerekiyordu. Ancak, en çok oylanan cevapların bazı sorunları olduğunu ve / veya çok şifreli veya aşırı karmaşık olduğunu buldum. Umarım bu başkalarının çözümü daha hızlı çözmesine yardımcı olur.

Öncelikle, @Hubert Grzeskowiak'ın çözümünün en basit çözüm olduğunu gördüm ve benim için çalıştı. Keşke ilk önce buna gelseydim. @Edward Campbell'ın cevabına dayanıyor, ancak döngü araması için karmaşık değil.

Ancak, en çok oy alan @ pushy'nin çözümüyle başladım. @Anonymous ve @Edward Campbell'ın bir birleşimidir. @pushy, hem Linux hem de Windows ortamlarını kapsamak için her iki yaklaşımın da gerekli olduğunu iddia ediyor. Ben OS X altında çalışan ve her ikisi de işe (bir kez @anonymous yaklaşım ile ilgili bir sorun düzeltildi) bulmak. Diğerlerinin de belirttiği gibi, bu çözüm çoğu zaman işe yarar, ancak hepsi işe yaramaz.

Ben karışıklığın çoğunun kaynağı @ anonymous'ın 'Çevre' alanında çalışan çözümünden geldiğini düşünüyorum. ProcessEnvironment yapısının tanımına bakıldığında , 'theEnvironment' bir Map <String, String> değil, bir Map <Değişken, Değer> 'dir. Haritayı temizlemek iyi çalışır, ancak putAll işlemi haritayı bir Harita <Dize, Dize> olarak yeniden oluşturur; bu, sonraki işlemler Harita <Değişken, Değer> 'i bekleyen normal API'yi kullanarak veri yapısı üzerinde çalıştığında sorunlara neden olabilir. Ayrıca, münferit elemanlara erişmek / kaldırmak bir problemdir. Çözüm 'TheEnvironment'a dolaylı olarak' The UnnodifiableEnvironment 'aracılığıyla erişmektir. Ancak bu bir tür Değiştirilemediğindenerişim, UnmodifiableMap türündeki 'm' özel değişkeni üzerinden yapılmalıdır. Aşağıdaki kodda getModifiableEnvironmentMap2 koduna bakın.

Benim durumumda, test için bazı ortam değişkenlerini kaldırmam gerekiyordu (diğerleri değişmemeli). Daha sonra testten sonra ortam değişkenlerini önceki durumlarına döndürmek istedim. Aşağıdaki rutinler bunu doğrudan yapar. Her iki sürümü de getModifiableEnvironmentMap'i OS X üzerinde test ettim ve her ikisi de eşdeğer çalışıyor. Bu konudaki yorumlara dayanmasına rağmen, ortama bağlı olarak biri diğerinden daha iyi bir seçim olabilir.

Not: Windows'a özgü gibi göründüğü için 'theCaseInsensitiveEnvironmentField' erişimine dahil etmedim ve test etmenin bir yolu yoktu, ancak eklemenin doğrudan doğru olması gerekir.

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

Teşekkürler, benim kullanım durumum ve mac os x altında da oldu.
Rafael Gonçalves

Bu kadar sevdim Groovy için biraz daha basit bir versiyona hışırtıyorum, aşağıya bakın.
mike kemirgen

4

Çevrimiçi ortamda dolaşmak, JNI ile bunu yapmak mümkün gibi görünüyor. Daha sonra C'den putenv () çağrısı yapmanız gerekir ve (muhtemelen) bunu hem Windows hem de UNIX üzerinde çalışacak şekilde yapmanız gerekir.

Tüm bunlar yapılabilirse, Java'nın beni düz bir cekete koymak yerine bunu desteklemesi çok zor olmazdı.

Başka bir yerde Perl konuşan bir arkadaş, bunun çevre değişkenlerinin küresel işlemesi ve Java'nın iyi bir tasarım için iyi bir izolasyon için çabalaması nedeniyle olduğunu öne sürüyor.


Evet, süreç ortamını C kodundan ayarlayabilirsiniz. Ama Java ile çalışmaya güvenmem. JVM'nin başlatma sırasında ortamı Java String nesnelerine kopyalaması için iyi bir olasılık vardır, bu nedenle değişiklikleriniz gelecekteki JVM işlemleri için kullanılmaz.
Darron

Uyarı için teşekkürler Darron. Muhtemelen haklı olmanız için iyi bir şans var.
skiphoppy

2
@Darron, bunu yapmak isteyecek birçok nedenin JVM'nin çevrenin ne olduğunu düşündüğü ile hiçbir ilgisi yoktur. ( Aramadan LD_LIBRARY_PATHönce ayarlamayı düşünün Runtime.loadLibrary(); dlopen()çağırdığı çağrı , Java'nın aynı fikrine değil , gerçek ortama bakar ).
Charles Duffy

Bu, yerel bir kütüphane tarafından başlatılan alt işlemler için çalışır (benim durumumda çoğu), ancak maalesef Java'nın Process veya ProcessBuilder sınıfları tarafından başlatılan alt işlemler için çalışmaz.
Dan

4

Pushy'nin cevabını yukarıdaki denedi ve çoğunlukla çalıştı. Ancak, bazı durumlarda, bu istisnayı görürdüm:

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

Dışarı Bu dönüşler belli iç sınıflara uygulanması nedeniyle, yöntem kereden fazla çağrıldı zaman ne ProcessEnvironment.olursa setEnv(..)tuşları den alınır yöntem birden çok kez denir, theEnvironmentharita, bunlar mevcut olması koşuluyla dizeleri (şimdi ilk çağrısına göre dizeler olarak setEnv(...)) ve haritanın Variable,özel bir iç sınıfı olan genel türüne dönüştürülemez .ProcessEnvironment.

Sabit bir versiyon (Scala'da) aşağıdadır. Umarım Java'ya geçmek çok zor değildir.

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 nerede tanımlanır?
Mike Slinn

1
Muhtemelen import java.lang.{Class => JavaClass}.
Randall Whitman

1
Java.lang.ProcessEnvironment uygulaması aynı platform için bile farklı platformlarda farklıdır. Örneğin, Windows uygulamasında java.lang.ProcessEnvironment $ Değişkeni sınıfı yoktur, ancak bu sınıf Linux için bir sınıfta bulunur. Kolayca kontrol edebilirsiniz. Linux için tar.gz JDK dağıtımını indirin ve kaynağı src.zip'ten çıkarın, ardından Windows dağıtımındaki aynı dosyayla karşılaştırın. JDK 1.8.0_181'de tamamen farklıdırlar. Onları Java 10'da kontrol etmedim, ancak aynı resim varsa şaşırmayacağım.
Alex Konshin

1

Bu @ pushy'nin kötü cevabının Kotlin kötü versiyonu =)

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

En azından macOS Mojave'de çalışıyor.


0

SpringBoot ile çalışıyorsanız, aşağıdaki özellikte ortam değişkenini belirtebilirsiniz:

was.app.config.properties.toSystemProperties

1
Lütfen biraz açıklayabilir misiniz?
Faraz

0

@ pushy'nin cevabına dayanan varyant , pencerelerde çalışır.

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)

Kullanımı:

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

0

Tim Ryan'ın yanıtı benim için çalıştı ... ama Groovy için istedim (örneğin Spock bağlamı) ve 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'deki bir sürüm, bu algoritmada ortamdan değişkenler ayarlamanıza ve ortamdan değişkenler almanıza izin veren bir dekoratör oluşturdum.

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

Son zamanlarda Edward'ın cevabına dayanarak yaptığım Kotlin uygulaması:

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

Parametreleri -D ile ilk java işleminize geçirebilirsiniz:

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

Değerler yürütme zamanında bilinmemektedir; kullanıcı bunları sağladığında / seçtiğinde programın yürütülmesi sırasında bilinir. Bu da ortam değişkenlerini değil, yalnızca sistem özelliklerini ayarlar.
skiphoppy

Daha sonra bu durumda muhtemelen alt işlemlerinizi çağırmak için düzenli bir yol (args [] parametresi aracılığıyla ana yönteme) bulmak istersiniz.
matt b

matt b, normal yol orijinal sorumda belirtildiği gibi ProcessBuilder üzerinden. :)
skiphoppy

7
-D parametreleri aracılığıyla kullanılabilir System.getPropertyve ile aynı değildir System.getenv. Ayrıca, Systemsınıf ayrıca bu özellikleri statik olarak kullanarak ayarlamanızı sağlarsetProperty
anirvan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.