Wie setze ich Umgebungsvariablen von Java aus? Ich sehe, dass ich dies für Unterprozesse mit tun kann ProcessBuilder
. Ich muss jedoch mehrere Unterprozesse starten, daher möchte ich lieber die Umgebung des aktuellen Prozesses ändern und die Unterprozesse diese erben lassen.
Es gibt eine System.getenv(String)
, um eine einzelne Umgebungsvariable zu erhalten. Ich kann auch einen Map
vollständigen Satz von Umgebungsvariablen mit abrufen System.getenv()
. Aber, ruft put()
auf , dass Map
wirft ein UnsupportedOperationException
- offenbar bedeuten sie für die Umwelt nur gelesen werden. Und es gibt keine System.setenv()
.
Gibt es also eine Möglichkeit, Umgebungsvariablen im aktuell ausgeführten Prozess festzulegen? Wenn das so ist, wie? Wenn nicht, was ist der Grund? (Liegt es daran, dass dies Java ist und ich daher keine bösen, nicht portierbaren, veralteten Dinge wie das Berühren meiner Umgebung tun sollte?) Und wenn nicht, gibt es gute Vorschläge für die Verwaltung der Änderungen der Umgebungsvariablen, die ich mehreren zuführen muss Unterprozesse?
quelle
Antworten:
Ich denke, Sie haben den Nagel auf den Kopf getroffen.
Ein möglicher Weg, um die Belastung zu verringern, wäre das Herausrechnen einer Methode
und führen Sie alle
ProcessBuilder
s durch, bevor Sie sie starten.Wahrscheinlich wissen Sie das auch schon, aber Sie können mehr als einen Prozess mit demselben starten
ProcessBuilder
. Wenn Ihre Unterprozesse identisch sind, müssen Sie dieses Setup nicht immer wieder durchführen.quelle
Für die Verwendung in Szenarien, in denen Sie bestimmte Umgebungswerte für Komponententests festlegen müssen, ist der folgende Hack möglicherweise hilfreich. Dadurch werden die Umgebungsvariablen in der gesamten JVM geändert (stellen Sie daher sicher, dass Sie alle Änderungen nach dem Test zurücksetzen), Ihre Systemumgebung wird jedoch nicht geändert.
Ich fand heraus, dass eine Kombination der beiden schmutzigen Hacks von Edward Campbell und anonym am besten funktioniert, da einer unter Linux nicht funktioniert, einer unter Windows 7 nicht. Um einen bösen Hack mit mehreren Plattformen zu erhalten, habe ich sie kombiniert:
Das funktioniert wie ein Zauber. Volle Credits an die beiden Autoren dieser Hacks.
quelle
import java.lang.reflect.Field;
Oder um eine einzelne Variable hinzuzufügen / zu aktualisieren und die Schleife gemäß dem Vorschlag von thejoshwolfe zu entfernen.
quelle
Class<?> cl = env.getClass();
stattdessen for-Schleife verwenden?quelle
Unter Android wird die Benutzeroberfläche über Libcore.os als eine Art versteckte API verfügbar gemacht.
Die Libcore-Klasse sowie das Schnittstellenbetriebssystem sind öffentlich. Nur die Klassendeklaration fehlt und muss dem Linker angezeigt werden. Es ist nicht erforderlich, die Klassen zur Anwendung hinzuzufügen, aber es tut auch nicht weh, wenn sie enthalten ist.
quelle
throws ErrnoException
durchthrows Exception
.Os.setEnv
jetzt. developer.android.com/reference/android/system/… , java.lang.String, boolean)Nur Linux
Festlegen einzelner Umgebungsvariablen (basierend auf der Antwort von Edward Campbell):
Verwendungszweck:
Fügen Sie die Methode zunächst in eine beliebige Klasse ein, z. B. SystemUtil. Dann nenne es statisch:
Wenn Sie danach anrufen
System.getenv("SHELL")
, werden Sie"/bin/bash"
zurückkommen.quelle
Dies ist eine Kombination aus der in Java konvertierten Antwort von @ paul-blair, die einige Bereinigungen enthält, auf die paul blair hingewiesen hat, und einige Fehler, die in @pushys Code enthalten zu sein scheinen, der aus @Edward Campbell besteht und anonym ist.
Ich kann nicht betonen, wie oft dieser Code NUR zum Testen verwendet werden sollte und ist extrem hackig. Aber für Fälle, in denen Sie die Umgebung in Tests einrichten müssen, ist es genau das, was ich brauchte.
Dies beinhaltet auch einige kleinere Details von mir, die es dem Code ermöglichen, unter beiden Windows-Betriebssystemen zu funktionieren
sowie Centos laufen weiter
Die Umsetzung:
quelle
Es stellt sich heraus, dass die Lösung von @ pushy / @ anonym / @ Edward Campbell unter Android nicht funktioniert, da Android nicht wirklich Java ist. Insbesondere hat Android überhaupt nicht
java.lang.ProcessEnvironment
. In Android ist dies jedoch einfacher. Sie müssen lediglich einen JNI-Aufruf an POSIX sendensetenv()
:In C / JNI:
Und in Java:
quelle
Wie die meisten Leute, die diesen Thread gefunden haben, habe ich einige Komponententests geschrieben und musste die Umgebungsvariablen ändern, um die richtigen Bedingungen für die Ausführung des Tests festzulegen. Ich fand jedoch, dass die am besten bewerteten Antworten einige Probleme hatten und / oder sehr kryptisch oder übermäßig kompliziert waren. Hoffentlich hilft dies anderen, die Lösung schneller zu finden.
Als erstes fand ich die Lösung von @Hubert Grzeskowiak am einfachsten und sie funktionierte für mich. Ich wünschte, ich wäre zuerst dazu gekommen. Es basiert auf der Antwort von @Edward Campbell, ohne jedoch die Suche nach Schleifen zu erschweren.
Ich habe jedoch mit der Lösung von @ pushy begonnen, die die meisten positiven Stimmen erhielt. Es ist eine Kombination aus @anonymous und @Edward Campbell's. @pushy behauptet, dass beide Ansätze erforderlich sind, um sowohl Linux- als auch Windows-Umgebungen abzudecken. Ich laufe unter OS X und stelle fest, dass beide funktionieren (sobald ein Problem mit dem @ anonymous-Ansatz behoben ist). Wie andere angemerkt haben, funktioniert diese Lösung die meiste Zeit, aber nicht alle.
Ich denke, die Ursache für den größten Teil der Verwirrung liegt in der Lösung von @onymous, die im Feld "theEnvironment" ausgeführt wird. Bei der Definition der ProcessEnvironment Struktur ist 'theEnvironment' keine Map <String, String>, sondern eine Map <Variable, Value>. Das Löschen der Map funktioniert einwandfrei, aber die putAll-Operation erstellt die Map neu als Map <String, String>, was möglicherweise zu Problemen führt, wenn nachfolgende Operationen die Datenstruktur mit der normalen API bearbeiten, die Map <Variable, Wert> erwartet. Auch der Zugriff auf / das Entfernen einzelner Elemente ist ein Problem. Die Lösung besteht darin, indirekt über 'die nicht veränderbare Umgebung' auf 'theEnvironment' zuzugreifen. Da dies aber ein Typ UnmodizableMap ist muss der Zugriff über die private Variable 'm' vom Typ UnmodizableMap erfolgen. Siehe getModutableEnvironmentMap2 im folgenden Code.
In meinem Fall musste ich einige der Umgebungsvariablen für meinen Test entfernen (die anderen sollten unverändert bleiben). Dann wollte ich die Umgebungsvariablen nach dem Test auf ihren vorherigen Zustand zurücksetzen. Die folgenden Routinen machen dies einfach. Ich habe beide Versionen von getModutableEnvironmentMap unter OS X getestet und beide funktionieren gleichwertig. Obwohl dies auf Kommentaren in diesem Thread basiert, ist je nach Umgebung möglicherweise eine bessere Wahl als die andere.
Hinweis: Ich habe keinen Zugriff auf das 'theCaseInsensitiveEnvironmentField' hinzugefügt, da dies Windows-spezifisch zu sein scheint und ich keine Möglichkeit hatte, es zu testen, aber das Hinzufügen sollte einfach sein.
quelle
Wenn Sie online stöbern, scheint es möglich zu sein, dies mit JNI zu tun. Sie müssten dann putenv () von C aus aufrufen, und Sie müssten dies (vermutlich) auf eine Weise tun, die sowohl unter Windows als auch unter UNIX funktioniert.
Wenn all das getan werden kann, wäre es für Java selbst sicherlich nicht zu schwierig, dies zu unterstützen, anstatt mich in eine Zwangsjacke zu stecken.
Ein Perl sprechender Freund an anderer Stelle schlägt vor, dass dies darauf zurückzuführen ist, dass Umgebungsvariablen prozessglobal sind und Java eine gute Isolation für ein gutes Design anstrebt.
quelle
LD_LIBRARY_PATH
vor dem AufrufRuntime.loadLibrary()
. Derdlopen()
aufgerufene Aufruf bezieht sich auf die reale Umgebung und nicht auf Javas Vorstellung davon.)Versuchte Pushys Antwort oben und es funktionierte größtenteils. Unter bestimmten Umständen würde ich jedoch diese Ausnahme sehen:
Dies stellt sich heraus, wenn die Methode aufgrund der Implementierung bestimmter innerer Klassen von mehr als einmal aufgerufen wurde.
ProcessEnvironment.
Wenn diesetEnv(..)
Methode mehr als einmal aufgerufen wird und die Schlüssel aus dertheEnvironment
Map abgerufen werden , handelt es sich nun um Zeichenfolgen (die eingegeben wurden) als Zeichenfolgen beim ersten Aufruf vonsetEnv(...)
) und kann nicht in den generischen Typ der Karte umgewandelt werden,Variable,
der eine private innere Klasse von istProcessEnvironment.
Eine feste Version (in Scala) finden Sie weiter unten. Hoffentlich ist es nicht allzu schwierig, auf Java zu übertragen.
quelle
import java.lang.{Class => JavaClass}
.Dies ist die böse Kotlin-Version der bösen Antwort von @ pushy =)
Es funktioniert zumindest in macOS Mojave.
quelle
Wenn Sie mit SpringBoot arbeiten, können Sie die Angabe der Umgebungsvariablen in der folgenden Eigenschaft hinzufügen:
quelle
jythonVariante basierend auf der Antwort von @ pushy , funktioniert unter Windows.
Verwendungszweck:
quelle
Tim Ryans Antwort hat bei mir funktioniert ... aber ich wollte sie für Groovy (zum Beispiel Spock-Kontext) und simplissimo:
quelle
In einer Version in Kotlin habe ich in diesem Algorithmus einen Dekorator erstellt, mit dem Sie Variablen festlegen und aus der Umgebung abrufen können.
quelle
Kotlin-Implementierung, die ich kürzlich basierend auf Edwards Antwort vorgenommen habe:
quelle
Sie können Parameter mit -D an Ihren anfänglichen Java-Prozess übergeben:
quelle
System.getProperty
und nicht identisch mitSystem.getenv
. Neben demSystem
erlaubt Klasse auch, diese Eigenschaften statischsetProperty