Jak ustawić zmienne środowiskowe z Java? Widzę, że mogę to zrobić dla podprocesów za pomocą ProcessBuilder
. Muszę jednak rozpocząć kilka podprocesów, więc wolę zmodyfikować środowisko bieżącego procesu i pozwolić, aby podprocesy go odziedziczyły.
Istnieje System.getenv(String)
możliwość uzyskania pojedynczej zmiennej środowiskowej. Mogę również uzyskać Map
pełny zestaw zmiennych środowiskowych za pomocą System.getenv()
. Ale przywołanie put()
tego Map
rzuca UnsupportedOperationException
- najwyraźniej oznacza to, że środowisko może być tylko do odczytu. I nie ma System.setenv()
.
Czy istnieje jakiś sposób na ustawienie zmiennych środowiskowych w aktualnie uruchomionym procesie? Jeśli tak to jak? Jeśli nie, jakie jest uzasadnienie? (Czy to dlatego, że jest to Java i dlatego nie powinienem robić złych, nieprzenoszalnych, przestarzałych rzeczy, takich jak dotykanie mojego środowiska?) A jeśli nie, jakieś dobre sugestie dotyczące zarządzania zmianami zmiennych środowiskowych, które będę musiał przekazać kilku podprocesy?
źródło
Odpowiedzi:
Myślę, że trafiłeś w sedno.
Możliwym sposobem na zmniejszenie obciążenia byłoby rozłożenie metody
i przejść
ProcessBuilder
przez to s przed ich uruchomieniem.Prawdopodobnie już o tym wiesz, ale możesz rozpocząć więcej niż jeden proces z tym samym
ProcessBuilder
. Więc jeśli twoje podprocesy są takie same, nie musisz wykonywać tej konfiguracji w kółko.źródło
Do użytku w scenariuszach, w których należy ustawić określone wartości środowiska dla testów jednostkowych, może okazać się przydatny następujący hack. Zmieni on zmienne środowiskowe w całej JVM (więc pamiętaj, aby zresetować wszelkie zmiany po teście), ale nie zmieni środowiska systemowego.
Odkryłem, że kombinacja dwóch brudnych hacków Edwarda Campbella i anonimowego działa najlepiej, ponieważ jeden z nich nie działa w systemie Linux, jeden nie działa pod Windows 7. Więc aby uzyskać wieloplatformowy hack zła, połączyłem je:
To działa jak urok. Pełne podziękowania dla dwóch autorów tych hacków.
źródło
import java.lang.reflect.Field;
Lub dodać / zaktualizować pojedynczy var i usunąć pętlę zgodnie z sugestią Joshwolfe.
źródło
Class<?> cl = env.getClass();
zamiast tego dla pętli?źródło
na Androidzie interfejs jest udostępniany przez Libcore.os jako rodzaj ukrytego API.
Klasa Libcore oraz system operacyjny interfejsu są publiczne. Brakuje tylko deklaracji klasy i należy ją pokazać konsolidatorowi. Nie trzeba dodawać klas do aplikacji, ale nie zaszkodzi, jeśli zostanie uwzględniona.
źródło
throws ErrnoException
zthrows Exception
.Os.setEnv
teraz. developer.android.com/reference/android/system/… , java.lang.String, boolean)Tylko Linux
Ustawianie pojedynczych zmiennych środowiskowych (na podstawie odpowiedzi Edwarda Campbella):
Stosowanie:
Najpierw umieść metodę w dowolnej klasie, np. SystemUtil. Następnie nazwij to statycznie:
Jeśli zadzwonisz
System.getenv("SHELL")
po tym,"/bin/bash"
wrócisz.źródło
Jest to kombinacja odpowiedzi @ paul-blair przekonwertowanej na Javę, która obejmuje kilka porządków wskazanych przez Paula Blair oraz błędy, które wydają się znajdować w kodzie @pushy, który składa się z @Edward Campbell i jest anonimowy.
Nie mogę podkreślić, jak bardzo ten kod powinien być użyty TYLKO podczas testowania i jest wyjątkowo hackerski. Ale w przypadkach, gdy potrzebujesz konfiguracji środowiska w testach, właśnie tego potrzebowałem.
Obejmuje to również niektóre moje drobne poprawki, które pozwalają na działanie kodu w obu systemach Windows
jak również uruchomione Centos
Implementacja:
źródło
Okazuje się, że rozwiązanie @ pushy / @ anonymous / @ Edward Campbell nie działa na Androidzie, ponieważ Android nie jest tak naprawdę Javą. W szczególności Android wcale nie ma
java.lang.ProcessEnvironment
. Ale w Androidzie okazuje się łatwiejsze, wystarczy wykonać wywołanie JNI do POSIXsetenv()
:W języku C / JNI:
I w Javie:
źródło
Jak większość ludzi, którzy znaleźli ten wątek, pisałem kilka testów jednostkowych i potrzebowałem zmodyfikować zmienne środowiskowe, aby ustawić odpowiednie warunki do uruchomienia testu. Jednak znalazłem, że najbardziej uprzywilejowane odpowiedzi miały pewne problemy i / lub były bardzo tajemnicze lub zbyt skomplikowane. Mamy nadzieję, że pomoże to innym w szybszym rozwiązaniu problemu.
Po pierwsze, w końcu uznałem rozwiązanie @Hubert Grzeskowiak za najprostsze i zadziałało dla mnie. Chciałbym najpierw do tego dojść. Opiera się na odpowiedzi @Edward Campbell, ale bez komplikacji dla wyszukiwania w pętli.
Zacząłem jednak od rozwiązania @ pushy, które uzyskało najwięcej pozytywnych opinii. Jest to kombinacja @anonymous i @Edward Campbell's. @pushy twierdzi, że oba podejścia są potrzebne, aby objąć zarówno środowisko Linux, jak i Windows. Pracuję pod OS X i stwierdzam, że oba działają (po rozwiązaniu problemu z podejściem @anonymous). Jak zauważyli inni, to rozwiązanie działa przez większość czasu, ale nie wszystkie.
Myślę, że źródłem większości nieporozumień jest rozwiązanie @ anonimowe działające w polu „Środowisko”. Patrząc na definicję struktury ProcessEnvironment , „środowisko” nie jest Mapą <Ciąg, Ciąg>, ale raczej Mapą <Zmienna, Wartość>. Czyszczenie mapy działa poprawnie, ale operacja putAll odbudowuje mapę Map <String, String>, co potencjalnie powoduje problemy, gdy kolejne operacje działają na strukturze danych przy użyciu normalnego interfejsu API, który oczekuje Map <Variable, Value>. Problemem jest także dostęp do / usuwanie poszczególnych elementów. Rozwiązaniem jest dostęp do „Środowiska” pośrednio poprzez „Środowisko modyfikowalne”. Ale ponieważ jest to typ UnmodifiableMapdostępu należy dokonać za pośrednictwem prywatnej zmiennej „m” typu UnmodifiableMap. Zobacz getModifiableEnvironmentMap2 w kodzie poniżej.
W moim przypadku musiałem usunąć niektóre zmienne środowiskowe z mojego testu (inne powinny pozostać niezmienione). Następnie chciałem przywrócić zmienne środowiskowe do ich poprzedniego stanu po teście. Poniższe procedury ułatwiają to. Przetestowałem obie wersje getModifiableEnvironmentMap na OS X i obie działają równorzędnie. Chociaż w oparciu o komentarze w tym wątku, jeden może być lepszym wyborem niż drugi, w zależności od środowiska.
Uwaga: Nie uwzględniłem dostępu do pola „theCaseInsensitiveEnvironmentField”, ponieważ wydaje się, że jest ono specyficzne dla systemu Windows i nie miałem możliwości go przetestować, ale dodanie go powinno być proste.
źródło
Grzebiąc w Internecie, wydaje się, że można to zrobić za pomocą JNI. Musiałbyś wtedy wywołać funkcję putenv () z C i (prawdopodobnie) musiałbyś to zrobić w sposób, który działał zarówno w systemie Windows, jak i UNIX.
Jeśli to wszystko można zrobić, z pewnością Java nie byłaby w stanie wesprzeć tego, zamiast wkładać mnie w prostą kurtkę.
Znajomy mówiący w Perlu gdzie indziej sugeruje, że dzieje się tak, ponieważ zmienne środowiskowe są przetwarzane globalnie, a Java dąży do dobrej izolacji dla dobrego projektu.
źródło
LD_LIBRARY_PATH
przed wywołaniemRuntime.loadLibrary()
;dlopen()
wywołanie, które wywołuje, patrzy na rzeczywiste środowisko, a nie na sam pomysł Javy).Próbowałem odpowiedzi pushy powyżej i zadziałało w większości. Jednak w niektórych okolicznościach widziałbym ten wyjątek:
Okazuje się to, gdy metoda została wywołana więcej niż raz, ze względu na implementację niektórych wewnętrznych klas.
ProcessEnvironment.
JeślisetEnv(..)
metoda jest wywoływana więcej niż raz, gdy klucze są pobierane ztheEnvironment
mapy, są one teraz ciągami znaków (zostały wstawione jako ciągi przy pierwszym wywołaniusetEnv(...)
) i nie mogą być rzutowane na ogólny typ mapy,Variable,
który jest prywatną klasą wewnętrznąProcessEnvironment.
Naprawiona wersja (w Scali) znajduje się poniżej. Mam nadzieję, że przeniesienie do Javy nie jest zbyt trudne.
źródło
import java.lang.{Class => JavaClass}
.Jest to zła wersja Kotlin od znaku @ nachalny za złą odpowiedź =)
Działa przynajmniej w systemie macOS Mojave.
źródło
Jeśli pracujesz z SpringBoot, możesz dodać określenie zmiennej środowiskowej w następującej właściwości:
źródło
Jythonwariant oparty na odpowiedzi @ pushy działa w systemie Windows.
Stosowanie:
źródło
Odpowiedź Tima Ryana działała dla mnie ... ale chciałem ją dla Groovy (na przykład kontekst Spocka) i simplissimo:
źródło
Wersja w Kotlinie, w tym algorytmie stworzyłem dekorator, który pozwala ustawiać i pobierać zmienne ze środowiska.
źródło
Implementacja Kotlina, którą niedawno wykonałem na podstawie odpowiedzi Edwarda:
źródło
Możesz przekazać parametry do początkowego procesu Java za pomocą -D:
źródło
System.getProperty
i nie są takie same jakSystem.getenv
. Poza tymSystem
klasa pozwala również ustawić te właściwości statycznie za pomocąsetProperty