392 votes

Comment définir les variables d'environnement à partir de Java ?

Comment définir les variables d'environnement à partir de Java ? Je vois que je peux le faire pour les sous-processus à l'aide des éléments suivants ProcessBuilder . Mais comme j'ai plusieurs sous-processus à lancer, je préfère modifier l'environnement du processus actuel et laisser les sous-processus en hériter.

Il y a un System.getenv(String) pour obtenir une seule variable d'environnement. Je peux également obtenir une Map de l'ensemble complet des variables d'environnement avec System.getenv() . Mais, en appelant put() sur ce point Map lance un UnsupportedOperationException -- apparemment, ils veulent que l'environnement soit en lecture seule. Et, il n'y a pas de System.setenv() .

Existe-t-il un moyen de définir des variables d'environnement dans le processus en cours d'exécution ? Si oui, comment ? Si non, quel est le raisonnement ? (Est-ce parce que c'est Java et donc que je ne devrais pas faire des choses obsolètes non portables comme toucher à mon environnement) ? Et sinon, avez-vous de bonnes suggestions pour gérer les changements de variables d'environnement que je vais devoir transmettre à plusieurs sous-processus ?

1 votes

System.getEnv() est destiné à être universel, certains environnements n'ont même pas de variables d'environnement.

17 votes

Pour tous ceux qui en ont eu besoin dans le cadre de tests unitaires : stackoverflow.com/questions/8168884/

1 votes

275voto

pushy Points 4646

Pour les scénarios dans lesquels vous devez définir des valeurs d'environnement spécifiques pour les tests unitaires, vous pouvez trouver le hack suivant utile. Il modifiera les variables d'environnement dans la JVM (veillez donc à réinitialiser les changements après votre test), mais ne modifiera pas l'environnement de votre système.

J'ai trouvé qu'une combinaison des deux hacks maléfiques d'Edward Campbell et d'anonymous fonctionne le mieux, car l'un d'eux ne fonctionne pas sous linux, l'autre ne fonctionne pas sous Windows 7. Donc pour obtenir un hack maléfique multiplateforme je les ai combinés :

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

Cela fonctionne comme un charme. Tous les crédits aux deux auteurs de ces hacks.

2 votes

Est-ce que cela ne changera que dans la mémoire, ou est-ce que cela changera toute la variable d'environnement dans le système ?

49 votes

Cela ne modifiera que la variable d'environnement en mémoire. C'est une bonne chose pour les tests, car vous pouvez définir la variable d'environnement comme nécessaire pour votre test, mais laisser les variables d'environnement dans le système telles qu'elles sont. En fait, je déconseille fortement à quiconque d'utiliser ce code à d'autres fins que les tests. Ce code est diabolique ;-)

14 votes

Pour information, la JVM crée une copie des variables d'environnement lorsqu'elle démarre. Cette opération modifiera cette copie, et non les variables d'environnement du processus parent qui a lancé la JVM.

109voto

Michael Myers Points 82361

(Est-ce parce que c'est Java et que, par conséquent, je ne devrais pas faire des choses obsolètes et non portables comme toucher à mon environnement) ?

Je pense que vous avez mis le doigt sur le problème.

Une façon possible d'alléger le fardeau serait de factoriser une méthode

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

et passer tout ProcessBuilder à travers elle avant de les démarrer.

De plus, vous le savez probablement déjà, mais vous pouvez lancer plus d'un processus avec le même ProcessBuilder . Ainsi, si vos sous-processus sont les mêmes, vous n'avez pas besoin d'effectuer cette configuration à plusieurs reprises.

1 votes

C'est une honte que la direction ne me laisse pas utiliser un langage portable différent pour exécuter cet ensemble de sous-processus diaboliques et obsolètes, alors :)

0 votes

@skiphoppy : aucun outil ne peut définir l'environnement du parent -- ce n'est pas un problème d'outil, c'est un problème de système d'exploitation.

23 votes

S.Lott, je ne cherche pas à définir l'environnement d'un parent. Je cherche à définir mon propre environnement.

90voto

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

Ou pour ajouter/mettre à jour une seule var et supprimer la boucle comme le suggère thejoshwolfe.

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

37voto

Hubert Grzeskowiak Points 5836

Linux/MacOS uniquement

Définition de variables d'environnement uniques (basé sur la réponse d'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);
    }
}

Utilisation :

Tout d'abord, placez la méthode dans la classe de votre choix, par exemple SystemUtil. Ensuite, appelez-la de manière statique :

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

Si vous appelez System.getenv("SHELL") après cela, vous obtiendrez "/bin/bash" arrière.

24voto

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X