Como faço para definir variáveis de ambiente do Java? Vejo que posso fazer isso para subprocessos usando ProcessBuilder
. No entanto, tenho vários subprocessos para iniciar, então prefiro modificar o ambiente do processo atual e deixar que os subprocessos o herdem.
Existe um System.getenv(String)
para obter uma única variável de ambiente. Também posso obter um Map
conjunto completo de variáveis de ambiente com System.getenv()
. Mas, chamando put()
isso Map
lança um UnsupportedOperationException
- aparentemente eles significam que o ambiente seja apenas para leitura. E não há System.setenv()
.
Então, existe alguma maneira de definir variáveis de ambiente no processo em execução no momento? Se sim, como? Se não, qual é a lógica? (É por ser Java e, portanto, eu não deveria estar fazendo coisas obsoletas não-portáveis como tocar meu ambiente?) E, se não, existem boas sugestões para gerenciar as variáveis de ambiente que eu precisarei alimentar várias subprocessos?
fonte
Respostas:
Eu acho que você acertou a unha na cabeça.
Uma maneira possível de aliviar o fardo seria fatorar um método
e passe todos os
ProcessBuilder
s antes de iniciá-los.Além disso, você provavelmente já sabe disso, mas pode iniciar mais de um processo com o mesmo
ProcessBuilder
. Portanto, se seus subprocessos forem os mesmos, você não precisará fazer essa configuração repetidamente.fonte
Para uso em cenários em que você precisa definir valores de ambiente específicos para testes de unidade, você pode achar útil o seguinte hack. Ele alterará as variáveis de ambiente em toda a JVM (certifique-se de redefinir as alterações após o teste), mas não alterará o ambiente do sistema.
Eu descobri que uma combinação dos dois hacks sujos de Edward Campbell e anônimos funciona melhor, pois um deles não funciona no linux, o computador não funciona no windows 7. Então, para obter um truque maligno multiplataforma, eu os combinei:
Isso funciona como um encanto. Créditos completos para os dois autores desses hacks.
fonte
import java.lang.reflect.Field;
Ou para adicionar / atualizar uma única var e remover o loop de acordo com a sugestão do thejoshwolfe.
fonte
Class<?> cl = env.getClass();
vez disso para loop?fonte
no Android, a interface é exposta via Libcore.os como um tipo de API oculta.
A classe Libcore e o SO da interface são públicos. Apenas a declaração de classe está ausente e precisa ser mostrada ao vinculador. Não há necessidade de adicionar as classes ao aplicativo, mas também não prejudica se estiver incluído.
fonte
throws ErrnoException
comthrows Exception
.Os.setEnv
agora. developer.android.com/reference/android/system/... , java.lang.String, boolean)Apenas Linux
Configurando variáveis de ambiente únicas (com base na resposta de Edward Campbell):
Uso:
Primeiro, coloque o método em qualquer classe que você desejar, por exemplo, SystemUtil. Em seguida, chame-o estaticamente:
Se você ligar
System.getenv("SHELL")
depois disso,"/bin/bash"
retornará.fonte
Essa é uma combinação da resposta de @ paul-blair convertida em Java, que inclui algumas limpezas apontadas por paul blair e alguns erros que parecem estar dentro do código do @pushy, que é composto por @Edward Campbell e anônimo.
Não posso enfatizar o quanto esse código SOMENTE deve ser usado nos testes e é extremamente hacky. Mas nos casos em que você precisa da configuração do ambiente em testes, é exatamente o que eu precisava.
Isso também inclui alguns pequenos toques meus que permitem que o código funcione no Windows em execução no
assim como o Centos rodando em
A implementação:
fonte
Acontece que a solução de @ pushy / @ anonymous / @ Edward Campbell não funciona no Android porque o Android não é realmente Java. Especificamente, o Android não tem
java.lang.ProcessEnvironment
. Mas acaba sendo mais fácil no Android, você só precisa fazer uma chamada JNI para POSIXsetenv()
:Em C / JNI:
E em Java:
fonte
Como a maioria das pessoas que encontrou esse encadeamento, eu estava escrevendo alguns testes de unidade e precisava modificar as variáveis de ambiente para definir as condições corretas para a execução do teste. No entanto, descobri que as respostas mais votadas tinham alguns problemas e / ou eram muito enigmáticas ou excessivamente complicadas. Espero que isso ajude outras pessoas a resolver a solução mais rapidamente.
Primeiro, finalmente achei a solução do @Hubert Grzeskowiak a mais simples e funcionou para mim. Eu gostaria de ter chegado a esse primeiro. É baseado na resposta de @Edward Campbell, mas sem a complicada pesquisa de loop.
No entanto, comecei com a solução @ pushy, que obteve o maior número de votos. É uma combinação de @anonymous e @Edward Campbell's. A @pushy afirma que as duas abordagens são necessárias para cobrir os ambientes Linux e Windows. Estou executando no OS X e acho que ambos funcionam (uma vez que um problema com a abordagem anônima é corrigido). Como outros observaram, essa solução funciona na maioria das vezes, mas não em todas.
Acho que a fonte da maior parte da confusão vem da solução da @ anonymous operando no campo 'theEnvironment'. Observando a definição da estrutura ProcessEnvironment , 'theEnvironment' não é um Mapa <String, String>, mas sim um Mapa <Variável, Valor>. A limpeza do mapa funciona bem, mas a operação putAll reconstrói o mapa como Map <String, String>, o que potencialmente causa problemas quando operações subsequentes operam na estrutura de dados usando a API normal que espera Map <Variable, Value>. Além disso, acessar / remover elementos individuais é um problema. A solução é acessar indiretamente o 'ambiente' através do 'ambiente não modificável'. Mas como esse é um tipo UnmodifiableMapo acesso deve ser feito através da variável privada 'm' do tipo UnmodifiableMap. Veja getModifiableEnvironmentMap2 no código abaixo.
No meu caso, eu precisei remover algumas das variáveis de ambiente para o meu teste (as outras devem permanecer inalteradas). Depois, quis restaurar as variáveis de ambiente ao seu estado anterior após o teste. As rotinas abaixo tornam isso fácil de fazer. Testei as duas versões do getModifiableEnvironmentMap no OS X e as duas funcionam de maneira equivalente. Embora com base nos comentários deste tópico, uma pode ser uma escolha melhor que a outra, dependendo do ambiente.
Nota: eu não incluí o acesso ao 'theCaseInsensitiveEnvironmentField', pois parece ser específico do Windows e não tinha como testá-lo, mas a adição deve ser direta.
fonte
Procurando on-line, parece que é possível fazer isso com o JNI. Você teria que fazer uma chamada para putenv () de C e (presumivelmente) teria que fazê-lo de uma maneira que funcionasse no Windows e no UNIX.
Se tudo isso puder ser feito, certamente não seria muito difícil para o próprio Java suportar isso em vez de me colocar em uma camisa de força.
Um amigo que fala Perl em outro lugar sugere que isso ocorre porque as variáveis de ambiente são globais do processo e o Java está buscando um bom isolamento para um bom design.
fonte
LD_LIBRARY_PATH
antes de chamarRuntime.loadLibrary()
; adlopen()
chamada que ele chama olha para o ambiente real , não para a idéia de Java sobre o mesmo).Tentei responder pushy acima e funcionou na maior parte. No entanto, em certas circunstâncias, eu veria essa exceção:
Isso acontece quando o método foi chamado mais de uma vez, devido à implementação de certas classes internas de
ProcessEnvironment.
Se osetEnv(..)
método é chamado mais de uma vez, quando as chaves são recuperadas dotheEnvironment
mapa, elas agora são cadeias (sendo inseridas em como strings pela primeira invocação desetEnv(...)
) e não pode ser convertida no tipo genérico do mapa,Variable,
que é uma classe interna privada deProcessEnvironment.
Uma versão fixa (em Scala) está abaixo. Espero que não seja muito difícil transferir para Java.
fonte
import java.lang.{Class => JavaClass}
.Esta é a versão má de Kotlin da resposta má de @ pushy =)
Está funcionando no macOS Mojave pelo menos.
fonte
Se você trabalha com o SpringBoot, pode adicionar a especificação da variável ambiental na seguinte propriedade:
fonte
jythonvariante baseada na resposta de @ pushy , funciona no Windows.
Uso:
fonte
A resposta de Tim Ryan funcionou para mim ... mas eu a queria para Groovy (contexto de Spock, por exemplo) e simplissimo:
fonte
Uma versão no Kotlin, neste algoritmo, criei um decorador que permite definir e obter variáveis do ambiente.
fonte
Implementação de Kotlin que fiz recentemente com base na resposta de Edward:
fonte
Você pode passar parâmetros para seu processo java inicial com -D:
fonte
System.getProperty
e não são os mesmos queSystem.getenv
. Além disso, aSystem
classe também permite definir essas propriedades estaticamente usandosetProperty