Defina Variáveis de Ambiente Variáveis no bash (ou outro)

8

Eu quero que meu script leia um arquivo contendo pares chave / valor de variáveis de ambiente para definir e, em seguida, defina-as.

Até agora, tenho isto:

#!/bin/bash

cat $1 | while read kv
do
    key=${kv%=*}
    val='echo ${kv#*=} | sed 's/^"\|"$//g''
    export $key="$val"
done

E eu quero ler um arquivo como este:

XAUTHLOCALHOSTNAME="localhost"
DISPLAY=":0"
XAUTHORITY="/tmp/some-XAuthority"

Eu só preciso dessas variáveis no escopo para a duração do script, por isso não preciso resolver o problema de definindo uma variável no script para o escopo pai .

Do meu teste, acho que meu problema está no export $key="$val" , então acho que só preciso de um substituto para essa linha.

    
por palswim 15.02.2012 / 23:40

4 respostas

10

export $key=$val deve funcionar bem em bash . Eu suspeito que seu problema é o pipe ( | ). Todos os comandos em um pipeline são executados em um subshell. No seu exemplo, a instância de bash que está executando seu script extrairá 2 subconsultas: uma para cat $1 e outra para while read ... . As variáveis são atribuídas e exportadas no subshell que executa o loop while e, em seguida, todas elas são prontamente descartadas quando a subshell sai. Uma maneira de consertar isso é não gerar nenhum subshell. Em vez de um uso inútil de cat , tente redirecionar:

while read ...; do
   ...
done < "$1"

O BashFAQ 24 explica isso com muito mais detalhes.

Uma abordagem alternativa é apenas a fonte do arquivo, com as exportações lançadas em:

. <(sed '/^export/!s/^/export /' "$1")

A <( ) é a substituição do processo.

Como uma observação adicional de cautela, como você está lendo variáveis de ambiente de um argumento, é necessário certificar-se de que o arquivo de argumento $1 seja uma fonte confiável, por isso não pode fazer mal ao seu script.

    
por 16.02.2012 / 00:07
2

( jw013 já deu uma resposta correta responder , mas quero salientar mais algumas questões.)

O lado direito de um pipeline é executado em seu próprio subshell no bash (assim como a maioria dos outros shells, as exceções sendo ATT ksh e zsh). Então você está definindo as variáveis de ambiente no subshell que executa o loop, mas não no processo principal do shell.

Aqui há uma correção fácil que substitui o uso inútil de cat por um redirecionamento:

while read …; do …; done <"$1"

Em geral, se você precisar pré-processar a entrada, coloque o loop e o restante do script (pelo menos a parte que precisa das variáveis alteradas) dentro de um bloco.

grep '^foo_' "$1" | {
  while read …; do …; done
  do_something
}

Observe que, em geral, while read line; do … não é muito interessante nas linhas de entrada. Consulte Por que o while IFS= read é usado com tanta frequência, em vez de IFS=; while read.. ? , para uma explicação. O idioma correto para iterar nas linhas de entrada é

while IFS= read -r line; do …

Aqui, a remoção de espaço em branco à esquerda e à direita não importa, pois não deve haver nenhuma entrada de amostra, mas você pode precisar de -r para evitar a expansão da barra invertida. É possível que while read line seja, na verdade, uma maneira correta de ler sua entrada (mas eu suspeito apenas se os valores das variáveis não contiverem novas linhas). Também é possível que seu formato de entrada seja ambíguo ou mais complexo de analisar. Verifique o que acontece se um dos valores das variáveis contiver um caractere de nova linha, uma aspa dupla ou uma barra invertida.

Um ponto em que você está definitivamente desconcertando a entrada é quando você extrai o valor:

val='echo ${kv#*=} | sed 's/^"\|"$//g''

Primeiro, você precisa usar aspas duplas em torno da substituição da variável: echo "${kv#*=}" ; caso contrário, o shell executa a divisão de palavras e geração de nome de arquivo (ou seja, globbing) nele. Segundo, echo não é uma maneira confiável de imprimir uma string, porque algumas strings que começam com - são tratadas como opções, e algumas shells executam a expansão de barra invertida no argumento. Uma maneira confiável e portátil de imprimir uma string é printf %s "${kv#*=}" . Além disso, se o valor abranger várias linhas, o programa sed atuará em cada uma separadamente, o que está errado. Isso é solucionável, mas há um método mais fácil, que é usar os recursos de manipulação de strings do shell: val=${kv#*=}; val=${val#\"}; val=${val%\"} .

Supondo que sua entrada seja analisada corretamente com um read simples, aqui está uma maneira mais simples de analisá-la, aproveitando a configuração IFS para dividir cada linha:

while read name value; do
  value=${value#\"}; value=${value%\"}
  export name="$value"
done <"$1"
    
por 16.02.2012 / 02:18
0

Outra boa opção pode ser colocar o export no arquivo e fornecê-lo:

export XAUTHLOCALHOSTNAME="localhost"
export DISPLAY=":0"
export XAUTHORITY="/tmp/some-XAuthority"

e depois:

. "$1"
    
por 19.11.2014 / 15:20
0

/ env

EXPORT=value
#NOTEXPORT=notvalue

script

#!/bin/sh

for entry in $(cat /env)
do
  if [[ ! $entry == \#* ]]
  then
    export $entry
  fi
done
    
por 31.10.2016 / 14:50