Qual é a melhor maneira de distribuir / independente de shell para definir variáveis ​​de ambiente?

31

A questão diz tudo. Atualmente, uso o Arch Linux e o zsh, mas gostaria de uma solução que (no mínimo) funcione tanto em VTs quanto em xterms e também (espero, de preferência) continuará funcionando se eu alternar distros ou shells.

Ouvi respostas bastante díspares para essa pergunta nos documentos de diferentes distros. O Ubuntu diz "use .pam_environment". Eu acho que no Arch o que eles recomendam depende do seu shell. Atualmente, eu coloco tudo no .profile e, se um shell não obtiver essa fonte por algum motivo (por exemplo, bash se o .bash_profile existir), eu o substituo manualmente. Mas parece que deve haver uma maneira melhor.

strugee
fonte
2
Isso não tem nada a ver com distro e tudo a ver com shell. Não tenho certeza se existe uma maneira portátil de fazer isso.
Joseph R.
Hmm. Awesome .
strugee

Respostas:

29

Infelizmente, não existe um local totalmente portátil para definir variáveis ​​de ambiente. Os dois arquivos que mais se aproximam são ~/.profile: o local tradicional e pronto para o uso em muitas configurações e ~/.pam_environmentuma alternativa moderna, comum, mas limitada.

O que colocar ~/.pam_environment

O arquivo ~/.pam_environmenté lido por todos os métodos de logon que usam o PAM e que têm esse arquivo ativado. Isso cobre a maioria dos sistemas Linux atualmente.

A principal vantagem ~/.pam_environmenté que (quando ativada) é lida antes do início do shell do usuário e, portanto, funciona independentemente do tipo de sessão, shell de login e outras complexidades. Até funciona para logins não interativos como su -c somecommande ssh somecommand.

A principal limitação ~/.pam_environmenté que você só pode colocar atribuições simples lá, não sintaxe de shell complexa. A sintaxe deste arquivo é a seguinte.

  • Os arquivos são analisados ​​linha por linha.
  • Os espaços em branco à esquerda são ignorados.
  • Opcionalmente, você pode iniciar linhas com exporte um único espaço (não uma guia, veja a figura).
  • Depois disso, cada linha deve ter a forma em VAR=VALUEque VAR consiste em letras, dígitos e sublinhados.
  • # inicia um comentário, ele não pode aparecer em um valor.
  • Se VALUE começar com 'ou "e contiver outra cotação idêntica, VAR será definido como a string entre as aspas (tudo após a segunda cotação ser ignorado). Caso contrário, o VAR é definido como a sequência após o =sinal.
  • Se não houver =, a variável é removida do ambiente.

Então, de cabeça para baixo, ~/.pam_environmentfunciona em uma grande variedade de circunstâncias. Por outro lado, você não pode ter configurações dinâmicas, como basear o valor de uma variável em outra variável (por exemplo, adicionar um diretório ao PATH) ou usar a saída de um comando (por exemplo, testar se um diretório ou programa está presente) e alguns caracteres ( #'", nova linha) são impossíveis ou problemáticos de colocar o valor.

O que colocar ~/.profile

Este arquivo deve ter sintaxe sh portátil (POSIX). Use somente extensões ksh ou bash (matrizes, [[ … ]]etc.) se você souber que seu sistema possui esses shells como /bin/sh.

Esse arquivo pode ser lido por scripts em aplicativos automatizados, portanto, não deve chamar programas que produzam qualquer saída ou chamada exec. Se você quiser fazer isso em logons no modo de texto, faça-o apenas para shells interativos. Exemplo:

case $- in *i*)
  # Display a message if I have new mail
  if mail -e; then echo 'You have new mail'; fi
  # If zsh is available, and this looks like a text-mode login, run zsh
  case "`ps $PPID` " in
    *" login "*)
      if type zsh >/dev/null 2>/dev/null; then exec zsh; fi;;
  esac
esac

Este é um exemplo de uso /bin/shcomo seu shell de login e alternar para o seu shell favorito. Veja também como posso usar o bash como meu shell de login quando meu sysadmin se recusa a me permitir alterá-lo

Quando ~/.profilenão é lido no login não gráfico?

Shell de login diferentes leem arquivos diferentes.

Se o seu shell de login for bash

O Bash lê ~/.bash_loginou ~/.bash_profilese eles existem, em vez de ~/.profile. Além disso, o bash não lê ~/.bashrcem um shell de login, mesmo que seja interativo. Para nunca mais precisar se lembrar dessas peculiaridades, crie a ~/.bash_profilecom as duas linhas a seguir:

. ~/.profile
case $- in *i*) . ~/.bashrc;; esac

Consulte também Quais arquivos de instalação devem ser usados ​​para configurar variáveis ​​de ambiente com o bash?

Se o seu shell de login for zsh

Zsh lê ~/.zprofilee ~/.zlogin, mas não ~/.profile. O Zsh tem uma sintaxe diferente de sh, mas pode ler ~/.profileno modo de emulação sh. Você pode usar isso para o seu ~/.zprofile:

emulate sh -c '. ~/.profile'

Veja também Zsh não atingindo ~ / .profile

Se o seu shell de login for algum outro shell

Não há muito o que fazer lá, além de usar /bin/shcomo seu shell de login e seu shell favorito (como peixe) apenas como um shell interativo. É o que eu faço com o zsh. Veja acima um exemplo de como chamar outro shell de ~/.profile.

Comandos remotos

Ao chamar um comando remoto sem passar por um shell interativo, nem todos os shells lêem um arquivo de inicialização.

O Ksh lê o arquivo especificado pela ENVvariável, se você conseguir passá-lo.

O Bash lê ~/.bashrcse não é interativo (!) E seu processo pai é chamado rshdou sshd. Então você pode começar ~/.bashrccom

if [[ $- != *i* ]]; then
  . ~/.profile
  return
fi

O Zsh sempre lê ~/.zshenvquando é iniciado. Use com cuidado, pois isso é lido por todas as instâncias do zsh, mesmo quando é um subshell em que você definiu outras variáveis. Se zsh é seu shell de login e você deseja usá-lo para definir variáveis ​​apenas para comandos remotos, use um protetor: defina algumas variáveis ~/.profile, como MY_ENVIRONMENT_HAS_BEEN_SET=yes, e verifique esse protetor antes de ler ~/.profile.

if [[ -z $MY_ENVIRONMENT_HAS_BEEN_SET ]]; then emulate sh -c '~/.profile'; fi

O caso de logins gráficos

Muitas distribuições, gerenciadores de exibição e ambientes de desktop organizam a execução ~/.profile, fornecendo explicitamente os scripts de inicialização ou executando um shell de login.

Infelizmente, não existe um método geral para lidar com combinações distro / DM / DE onde ~/.profilenão é lido.

Se você usar uma sessão tradicional iniciada por ~/.xsession, este é o local em que você deve definir suas variáveis ​​de ambiente; faça isso por meio de sourcing ~/.profile(ie . ~/.profile). Observe que, em algumas configurações, os scripts de inicialização do ambiente de área de trabalho serão originados ~/.profilenovamente.

Gilles 'SO- parar de ser mau'
fonte
o que case $- in *i*)faz?
Qodeninja 5/02
2
@qodeninja Executa as seguintes instruções (até a correspondência ;;ou esac) se $-corresponder ao padrão *i*, ou seja, se $-contiver i, ou seja, se o shell for interativo.
Gilles 'SO- stop be evil'
$-é uma sequência de opções de shell definidas no momento. (como set -x). isignifica shell interativo.
Peter Cordes
Você não pode simplesmente obter um arquivo comum, digamos ~/.config/env, mesmo sem emulação?
Kevin Suttle
11
@ StéphaneChazelas Essa é uma visão purista. Eu mantenho minha .profileconformidade com conchas Bourne bastante antigas, mas reconheço que algumas pessoas simplesmente não se importam. Não tenho nada contra as pessoas supondo que sh = bash para seus próprios arquivos, só me importo se eles publicarem #!/bin/shscripts que usem recursos do bash.
Gilles 'SO- stop be evil'
4

Até onde eu sei, não existe um padrão agnóstico de distribuição e shell como definir variáveis ​​de ambiente.

O padrão mais comum e de fato parece ser /etc/profilee ~/.profile. O segundo mais comum parece ser /etc/environmente ~/.pam_environment.

Parece-me que toda a documentação que eu encontrei também já foi encontrada. Eu os listo aqui de qualquer maneira para os outros leitores.

  • O Debian recomenda /etc/profilee ~/.profile( link ).
  • O Ubuntu recomenda /etc/environmente ~/.pam_environment( link ).
  • O Arch Linux menciona, entre outros, /etc/profilee /etc/environment( link ).

Bônus: um texto questionando o uso e / ou uso indevido do /etc/environmentdebian ( link , última atualização 2008).

lesmana
fonte
Independentemente do arquivo que você usa, você ainda encontra uma sintaxe incompatível entre diferentes shells.
Joseph R.
11
@JosephR. a maioria dos reservatórios não mantém compatibilidade com versões anteriores sh? Enquanto você se apegar ao POSIX, eu pensaria que você ficaria bem ...
evilsoup
11
AFAIK você pode variáveis não atribuir em cshe amigos o caminho POSIX (você precisa de algo como setou setenv)
Joseph R.
0

Adicionei o seguinte script ~ / bin / agnostic_setenv:

#!/bin/csh -f
set args = ($*)
if ($#args == 1) then
   echo "export $args[1]="
   exit 0
endif

if ($#args == 2) then
   if ("$args[1]" =~ *csh*) then 
      echo "setenv $args[2]"
      exit 0
   else
      echo "export $args[1]=$args[2]"
      exit 0
   endif
endif

echo "setenv $args[2] $args[3]"

E em ~ / .perl-homedir eu uso:

eval `${HOME}/bin/agnostic_setenv $shell PERL_HOMEDIR 0`

Um script semelhante para agnostic_unsetenv:

#!/bin/csh -f
set args = ($*)
if ($#args == 1) then
   echo "export $args[1]"
   exit 0
endif

echo "unsetenv $args[2]"
exit 0
Kobi
fonte