मैं जावा से पर्यावरण चर कैसे सेट करूं?


289

मैं जावा से पर्यावरण चर कैसे सेट करूं? मैं देख रहा हूँ कि मैं उपप्रकारों के उपयोग के लिए ऐसा कर सकता हूँ ProcessBuilder। मेरे पास शुरू करने के लिए कई उपप्रकार हैं, हालांकि, मैं वर्तमान प्रक्रिया के वातावरण को संशोधित करना चाहता हूं और उपप्रोसेस को विरासत में देना चाहिए।

वहाँ System.getenv(String)एक एकल पर्यावरण चर प्राप्त करने के लिए है। मुझे Mapपर्यावरण चर का पूरा सेट भी मिल सकता है System.getenv()। लेकिन, put()उस पर कॉल करना Mapएक UnsupportedOperationException- जाहिर है कि वे पर्यावरण के लिए केवल पढ़ने के लिए मतलब है। और, वहाँ नहीं है System.setenv()

तो, क्या वर्तमान में चल रही प्रक्रिया में पर्यावरण चर सेट करने का कोई तरीका है? यदि हां, तो कैसे? यदि नहीं, तो तर्क क्या है? (क्या यह इसलिए है क्योंकि यह जावा है और इसलिए मुझे अपने वातावरण को छूने जैसी अनिर्वचनीय अप्रचलित चीजों को नहीं करना चाहिए?) और यदि नहीं, तो पर्यावरण चर परिवर्तन के प्रबंधन के लिए कोई अच्छा सुझाव जो मुझे कई को खिलाने की आवश्यकता है subprocesses?


System.getEnv () को सार्वभौमिक-ईश माना जाता है, कुछ वातावरण में पर्यावरण चर भी नहीं होते हैं।
b1nary.atr0phy

7
इकाई परीक्षण उपयोग के मामले में किसी को भी इसकी आवश्यकता होती है: stackoverflow.com/questions/8168884/…
Atifm

स्काला के लिए, इसका उपयोग करें: gist.github.com/vpatryshev/b1bbd15e2b759c157b58b68c58891ff4
व्लाद पैट्रिसेव

जवाबों:


88

(क्या यह इसलिए है क्योंकि यह जावा है और इसलिए मुझे अपने वातावरण को छूने जैसी अनिर्वचनीय अप्रचलित चीजों को नहीं करना चाहिए?)

मुझे लगता है कि आपने सिर पर कील ठोक दी है।

बोझ को कम करने का एक संभव तरीका एक विधि का कारक होगा

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

और ProcessBuilderउन्हें शुरू करने से पहले इसके माध्यम से किसी भी एस पास करें।

इसके अलावा, आप शायद यह पहले से ही जानते हैं, लेकिन आप एक से अधिक प्रक्रिया शुरू कर सकते हैं ProcessBuilder। इसलिए यदि आपके उपप्रक्रम समान हैं, तो आपको इस सेटअप को बार-बार करने की आवश्यकता नहीं है।


1
यह शर्म की बात है कि मुझे इस बुरी, अप्रचलित उपप्रकार के सेट को चलाने के लिए एक अलग पोर्टेबल भाषा का उपयोग नहीं करने देगा। :)
स्किपहोप्पी नो

18
S.Lott, मैं माता-पिता के वातावरण को सेट करना नहीं चाह रहा हूं। मैं अपना वातावरण सेट करना चाह रहा हूं।
18

3
यह बहुत अच्छा काम करता है, जब तक कि यह किसी और की लाइब्रेरी (जैसे कि सन) की प्रक्रिया शुरू न हो।
सुलिवन- ३६

24
@ b1naryatr0phy आप इस बिंदु से चूक गए। कोई भी आपके पर्यावरण चर के साथ नहीं खेल सकता है क्योंकि वे चर एक प्रक्रिया के लिए स्थानीय हैं (जो आप विंडोज में सेट करते हैं डिफ़ॉल्ट मान हैं)। प्रत्येक प्रक्रिया अपने स्वयं के चर को बदलने के लिए स्वतंत्र है ... जब तक कि इसका जावा।
मआर्टिनस

9
जावा की यह सीमा थोड़ा सा बाहर है। जावा के लिए कोई कारण नहीं है कि आप "इसके अलावा जावा को सेट न करें क्योंकि हम जावा को ऐसा नहीं करना चाहते हैं"।
इयानॉर्टन

232

उन परिदृश्यों में उपयोग के लिए जहां आपको यूनिट परीक्षणों के लिए विशिष्ट पर्यावरण मान निर्धारित करने की आवश्यकता होती है, आपको निम्नलिखित हैक उपयोगी मिल सकते हैं। यह पूरे जेवीएम में पर्यावरण चर को बदल देगा (इसलिए सुनिश्चित करें कि आप अपने परीक्षण के बाद किसी भी परिवर्तन को रीसेट कर देंगे), लेकिन आपके सिस्टम के वातावरण में परिवर्तन नहीं करेगा।

मैंने पाया कि एडवर्ड कैंपबेल और अनाम द्वारा दो गंदे हैक्स का एक संयोजन सबसे अच्छा काम करता है, क्योंकि एक लिनक्स के तहत काम नहीं करता है, एक विंडोज 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
क्या यह केवल मेमोरी में बदल जाएगा, या वास्तव में सिस्टम में पूरे पर्यावरण चर को बदल देगा?
शेरविन असगरी

36
यह केवल मेमोरी में पर्यावरण चर को बदल देगा। यह परीक्षण के लिए अच्छा है, क्योंकि आप पर्यावरण चर को अपने परीक्षण के लिए आवश्यक रूप से सेट कर सकते हैं, लेकिन सिस्टम में एनव्स को छोड़ दें जैसे वे हैं। वास्तव में, मैं परीक्षण के अलावा किसी अन्य उद्देश्य के लिए इस कोड का उपयोग करने से किसी को भी हतोत्साहित करूंगा। यह कोड दुष्ट है ;-)
pushy

9
FYI के रूप में, JVM पर्यावरण चर की एक प्रति बनाता है जब यह शुरू होता है। यह उस प्रति को संपादित करेगा, जेवीएम शुरू करने वाली मूल प्रक्रिया के लिए पर्यावरण चर नहीं।
bmeding

मैं Android पर यह कोशिश की और यह लेने के लिए प्रतीत नहीं किया। किसी और को Android पर किसी भी भाग्य है?
हंस-क्रिस्टोफ़ स्टीनर

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

या एकल संस्करण को जोड़ने / अद्यतन करने और जोशवॉल्फ के सुझाव के अनुसार लूप को हटाने के लिए।

@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
ऐसा लगता है कि स्मृति में नक्शे को संशोधित करेगा, लेकिन क्या यह सिस्टम को मूल्य बचाएगा?
जॉन Onstott

1
अच्छी तरह से यह पर्यावरण चर के स्मृति मानचित्र को बदलता है। मुझे लगता है कि बहुत सारे उपयोग-मामलों में ग्रस्त है। @ एडवर्ड - गोश, यह कल्पना करना मुश्किल है कि यह समाधान पहली बार में कैसे पता चला था!
अनिरवन २ an

13
यह सिस्टम पर पर्यावरण चर को नहीं बदलेगा, लेकिन जावा के वर्तमान आह्वान में उन्हें बदल देगा। यह इकाई परीक्षण के लिए बहुत उपयोगी है।
स्टुअर्ट के

10
Class<?> cl = env.getClass();लूप के लिए इसके बजाय उपयोग क्यों नहीं ?
thejoshwolfe

1
यह वही है जिसकी मुझे तलाश है! मैं कुछ कोड के लिए एकीकरण परीक्षण लिख रहा हूं जो किसी तीसरे पक्ष के टूल का उपयोग करता है, जो किसी कारण से, आपको पर्यावरणीय चर के साथ अपनी बेतुकी छोटी डिफ़ॉल्ट टाइमआउट लंबाई को संशोधित करने देता है।
डेविड डीमर

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

एंड्रॉइड पर इंटरफ़ेस को 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
डेविसएनटी

7
एपीआई 21, Os.setEnvअब है। developer.android.com/reference/android/system/… , java.lang.String, बूलियन)
जारेड बड्स

1
पाई के नए प्रतिबंधों के साथ अब संभावित रूप से
अशुद्ध

13

केवल लिनक्स

एकल पर्यावरण चर की स्थापना (एडवर्ड कैंपबेल के उत्तर पर आधारित):

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.setEnv("SHELL", "/bin/bash");

यदि आप इसके System.getenv("SHELL")बाद कॉल करते हैं, तो आपको "/bin/bash"वापस मिल जाएगा।


उपरोक्त विंडोज़ 10 में काम नहीं करता है, लेकिन लिनक्स में काम करेगा
मेंगचेंगफेंग

दिलचस्प। मैंने इसे स्वयं विंडोज पर आजमाया नहीं था। क्या आपको कोई त्रुटि मिलती है, @mengchengfeng?
ह्यूबर्ट ग्रेज्सकोविएक

@HubertGrzeskowiak हमने कोई त्रुटि संदेश नहीं देखा, यह सिर्फ काम नहीं किया ...
mengchengfeng

9

यह जावा में परिवर्तित @ पाऊल-ब्लेयर के उत्तर का एक संयोजन है, जिसमें पॉल ब्लेयर द्वारा बताए गए कुछ क्लीनअप शामिल हैं और कुछ गलतियाँ जो @pushy के कोड के अंदर हुई हैं, जो @ एडवर्ड कैंपबेल और अनाम से बना है।

मैं इस बात पर जोर नहीं दे सकता कि इस कोड को केवल परीक्षण में कैसे इस्तेमाल किया जाना चाहिए और यह बहुत हैक है। लेकिन ऐसे मामलों के लिए जहां आपको परीक्षणों में पर्यावरण सेटअप की आवश्यकता होती है, यह वही है जो मुझे चाहिए था।

इसमें मेरा कुछ मामूली स्पर्श भी शामिल है जो कोड को विंडोज पर चलने वाले दोनों पर काम करने की अनुमति देता है

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 / @ अनाम / @ एडवर्ड कैम्पबेल से समाधान एंड्रॉइड पर काम नहीं करता है क्योंकि एंड्रॉइड वास्तव में जावा नहीं है। विशेष रूप से, एंड्रॉइड के पास बिल्कुल नहीं है java.lang.ProcessEnvironment। लेकिन एंड्रॉइड में यह आसान हो जाता है, आपको बस PNIX पर एक जेएनआई कॉल करने की आवश्यकता है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;
}

और जावा में:

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 का समाधान सबसे सरल पाया और यह मेरे लिए काम कर गया। काश मैं पहले उस पर आ जाता। यह @ एडवर्ड कैंपबेल के उत्तर पर आधारित है, लेकिन लूप खोज के लिए जटिल नहीं है।

हालांकि, मैंने @ पुसी के समाधान के साथ शुरुआत की, जिसे सबसे अधिक उत्थान मिला। यह @ बेनामी और @ एडवर्ड कैंपबेल का कॉम्बो है। @pushy का दावा है कि दोनों दृष्टिकोणों को लिनक्स और विंडोज वातावरण दोनों को कवर करने की आवश्यकता है। मैं ओएस एक्स के तहत चल रहा हूं और पाता हूं कि दोनों काम (एक बार @anonymous दृष्टिकोण के साथ एक मुद्दा तय हो गया है)। जैसा कि दूसरों ने नोट किया है, यह समाधान ज्यादातर समय काम करता है, लेकिन सभी नहीं।

मुझे लगता है कि अधिकांश भ्रम का स्रोत 'गुमनाम समाधान' क्षेत्र में @ बेनामी समाधान के संचालन से आता है। ProcessEnvironment संरचना , 'theEnvironment' कोई नक्शा <स्ट्रिंग, स्ट्रिंग> नहीं है, बल्कि यह एक नक्शा <Variable, Value> है। मानचित्र को साफ़ करना ठीक काम करता है, लेकिन putAll ऑपरेशन मानचित्र को मानचित्र <String, String> से पुनः जोड़ता है, जो संभावित रूप से तब समस्याएँ पैदा करता है जब बाद के संचालन सामान्य API का उपयोग करके डेटा संरचना पर कार्य करते हैं जो मानचित्र <Variable, Value> की अपेक्षा करता है। इसके अलावा, व्यक्तिगत तत्वों तक पहुंच / हटाना एक समस्या है। इसका समाधान अप्रत्यक्ष रूप से 'theUnmodifiableEnvironment' के माध्यम से 'theEnvironment' तक पहुँचना है। लेकिन चूंकि यह एक प्रकार है की परिभाषा को देखते UnmodifiableMap हैहुए पहुँच को UnmodifiableMap प्रकार के निजी चर 'm' के माध्यम से किया जाना चाहिए। नीचे दिए गए कोड में getModifiableEnvironmentMap2 देखें।

मेरे मामले में मुझे अपने परीक्षण के लिए कुछ पर्यावरण चर निकालने की जरूरत थी (अन्य को अपरिवर्तित होना चाहिए)। तब मैं परीक्षण के बाद पर्यावरण चर को उनकी पूर्व स्थिति में पुनर्स्थापित करना चाहता था। नीचे दी गई दिनचर्या उस कार्य को सीधे करने के लिए आगे करती है। मैंने OS X पर getModifiableEnvironmentMap के दोनों संस्करणों का परीक्षण किया, और दोनों समान रूप से काम करते हैं। हालांकि इस सूत्र में टिप्पणियों के आधार पर, एक पर्यावरण के आधार पर दूसरे की तुलना में बेहतर विकल्प हो सकता है।

नोट: मैंने 'theCaseInsensitiveEnvironmentField' तक पहुँच को शामिल नहीं किया है क्योंकि यह विंडोज विशिष्ट प्रतीत होता है और मेरे पास इसका परीक्षण करने का कोई तरीका नहीं था, लेकिन इसे जोड़ना सीधे आगे होना चाहिए।

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

धन्यवाद, यह वास्तव में मेरा उपयोग मामला था और मैक ओएस एक्स के तहत भी।
राफेल गोंकालेव्स

इसे इतना पसंद किया कि मैंने ग्रूवी के लिए थोड़ा सरल संस्करण तैयार किया, नीचे देखें।
माणिक कृंतक

4

ऑनलाइन घूमते हुए, ऐसा लगता है कि जेएनआई के साथ ऐसा करना संभव हो सकता है। फिर आपको C से पुटीनव () को कॉल करना होगा, और आपको (संभवतः) इसे एक तरह से करना होगा जो विंडोज और यूनिक्स दोनों पर काम करता है।

अगर यह सब किया जा सकता है, तो निश्चित रूप से जावा के लिए यह बहुत मुश्किल नहीं होगा कि वह मुझे एक सीधी जैकेट में रखने के बजाय इसका समर्थन करे।

एक पर्ल-बोलने वाला दोस्त कहीं और बताता है कि ऐसा इसलिए है क्योंकि पर्यावरण चर वैश्विक प्रक्रिया है और जावा अच्छे डिजाइन के लिए अच्छे अलगाव के लिए प्रयास कर रहा है।


हां, आप C कोड से प्रक्रिया वातावरण सेट कर सकते हैं। लेकिन मैं जावा में काम करने पर भरोसा नहीं करूंगा। एक अच्छा मौका है कि स्टार्टअप के दौरान जेवीएम जावा स्ट्रिंग वस्तुओं में पर्यावरण की नकल करता है, इसलिए आपके परिवर्तनों का उपयोग भविष्य के जेवीएम संचालन के लिए नहीं किया जाएगा।
डैरॉन

चेतावनी के लिए धन्यवाद, डैरन। वहाँ शायद एक अच्छा मौका तुम सही हो।
स्किप्होपी

2
@Darron कारणों में से कई यह करना चाहते हैं कि जेवीएम को लगता है कि पर्यावरण के साथ क्या करना है, इसके लिए कुछ भी नहीं है। ( LD_LIBRARY_PATHकॉल करने से पहले सेटिंग के बारे में सोचें Runtime.loadLibrary(); dlopen()यह जो कॉल करता है वह वास्तविक वातावरण में दिखता है, जावा के विचार पर नहीं)।
चार्ल्स डफी

यह एक देशी पुस्तकालय (जो मेरे मामले में उनमें से ज्यादातर है) द्वारा शुरू की गई उपप्रकारों के लिए काम करता है, लेकिन दुर्भाग्यवश जावा के प्रोसेस या प्रोसेसब्यूस्टल वर्गों द्वारा शुरू किए गए उपप्रकारों के लिए काम नहीं करता है।
Dan

4

ऊपर पुसी के जवाब की कोशिश की और इसने अधिकांश भाग के लिए काम किया। हालाँकि, कुछ परिस्थितियों में, मुझे यह अपवाद दिखाई देगा:

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

यह तब होता है जब विधि को एक से अधिक बार बुलाया गया था, कुछ आंतरिक वर्गों के कार्यान्वयन के कारण ProcessEnvironment.यदि setEnv(..)विधि को एक से अधिक बार कहा जाता है, जब चाबियाँ theEnvironmentमानचित्र से पुनर्प्राप्त की जाती हैं, तो वे अब तार कर दी जाती हैं (होने में डाल दी जाती हैं) के पहले आह्वान द्वारा तार के रूप में setEnv(...)) और नक्शे के सामान्य प्रकार में नहीं डाला जा सकता है, Variable,जो कि एक निजी आंतरिक वर्ग हैProcessEnvironment.

एक निश्चित संस्करण (स्काला में), नीचे है। उम्मीद है कि जावा में ले जाना बहुत मुश्किल नहीं है।

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 को कहाँ परिभाषित किया गया है?
माइक स्लाइन

1
संभवतः import java.lang.{Class => JavaClass}
रान्डेल व्हिटमैन

1
Java.lang.ProcessEnvironment का कार्यान्वयन एक ही निर्माण के लिए भी अलग-अलग प्लेटफार्मों पर अलग है। उदाहरण के लिए, विंडोज के कार्यान्वयन में कोई वर्ग java.lang.ProcessEnvironment $ Variable नहीं है, लेकिन लिनक्स के लिए यह वर्ग एक में मौजूद है। आप इसे आसानी से जांच सकते हैं। बस लिनक्स के लिए tar.gz JDK वितरण को डाउनलोड करें और src.zip से स्रोत को निकालें और फिर विंडोज के लिए वितरण से उसी फ़ाइल के साथ तुलना करें। वे JDK 1.8.0_181 में बिल्कुल अलग हैं। मैंने उन्हें जावा 10 में चेक नहीं किया है लेकिन अगर एक ही तस्वीर है तो मुझे आश्चर्य नहीं होगा।
एलेक्स कोन्शिन

1

यह @ पुष्य के दुष्ट उत्तर = के कोटलिन बुराई संस्करण है )

@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

यदि आप स्प्रिंगबूट के साथ काम करते हैं तो आप निम्नलिखित संपत्ति में पर्यावरण चर को निर्दिष्ट कर सकते हैं:

was.app.config.properties.toSystemProperties

1
क्या आप कृपया थोड़ा समझा सकते हैं?
फराज

0

@ pushy के उत्तर पर आधारित संस्करण , विंडोज़ पर काम करता है।

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

टिम रयान के जवाब ने मेरे लिए काम किया ... लेकिन मैं इसे ग्रूवी के लिए चाहता था (उदाहरण के लिए स्पोक संदर्भ), और सिंपिसिमो:

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

कोटलिन में एक संस्करण, इस एल्गोरिथ्म में मैंने एक डेकोरेटर बनाया जो आपको पर्यावरण से चर सेट करने और प्राप्त करने की अनुमति देता है।

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

कोटलिन कार्यान्वयन मैंने हाल ही में एडवर्ड के उत्तर के आधार पर बनाया है:

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 -cp <classpath> -Dkey1=value -Dkey2=value ...

निष्पादन समय पर मान ज्ञात नहीं हैं; वे प्रोग्राम के निष्पादन के दौरान ज्ञात हो जाते हैं जब उपयोगकर्ता उन्हें प्रदान / चयन करता है। और यह केवल सिस्टम गुण सेट करता है, न कि पर्यावरण चर।
स्किपपॉपी

फिर उस मामले में आप शायद अपने उपप्रजातियों को आमंत्रित करने के लिए एक नियमित तरीका ढूंढना चाहते हैं (आर्ग के माध्यम से [मुख्य विधि के लिए)।
मैट बी

मैट बी, नियमित रूप से प्रोसेसब्युलर के माध्यम से है, जैसा कि मेरे मूल प्रश्न में उल्लेखित है। :)
स्किप्पॉपी 18

7
-D मापदंडों के माध्यम से उपलब्ध हैं System.getPropertyऔर के रूप में ही नहीं हैं System.getenv। इसके अलावा, Systemवर्ग भी इन गुणों को सांख्यिकीय रूप से सेट करने की अनुमति देता हैsetProperty
अनिरवन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.