Definir variáveis ​​de ambiente variáveis ​​no bash (ou outro)

9

Quero que meu script leia um arquivo contendo pares de chave / valor de variáveis ​​de ambiente para definir e depois defini-los.

Até agora, eu tenho o seguinte:

#!/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 durante a duração do script, portanto não preciso resolver o problema de definir uma variável no script para o escopo pai .

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

palswim
fonte
um pouco fora de tópico, mas desde que fiquei preso ao tentar imitar seu script, estou compartilhando ... ao pegar uma chave ... usar key=${kv%%=*}é melhor do que key=${kv%=*}porque %% e ## diferem de% e # para saber se a parte removida é a parte mais curta ou mais longa removível
pulkitsinghal

Respostas:

10

export $key=$valdeve funcionar muito bem bash. Suspeito que seu problema seja o pipe ( |). Todos os comandos em um pipeline são executados em um subshell. No seu exemplo, a instância bashque está executando o seu script dividirá 2 subshells: um para cat $1e outro para o while read .... As variáveis ​​são atribuídas e exportadas no subshell executando o loop while e, em seguida, todas elas são lançadas imediatamente quando o subshell sai. Uma maneira de corrigir isso é não gerar sub-conchas. Em vez de um uso inútil de gato , tente o redirecionamento:

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

O BashFAQ 24 explica isso com muito mais detalhes.

Uma abordagem alternativa é apenas originar o arquivo, com as exportações lançadas em:

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

A <( )é substituição processo.

Como uma nota adicional de cautela, como você está lendo variáveis ​​de ambiente de um argumento, certifique-se de que o arquivo de argumento $1seja uma fonte confiável, para que não possa fazer coisas ruins ao seu script.

jw013
fonte
Eu usei o redirecionamento anteriormente; outra coisa não deve ter funcionado quando mudei o script para usar cat. Mas, o script com redirecionamento funciona agora, obrigado!
palswim
Uso inútil de gato considerado prejudicial!
Scott
2

( jw013 já deu uma resposta correta , mas quero apontar mais alguns problemas.)

O lado direito de um pipeline é executado em seu próprio subshell no bash (assim como na maioria dos outros shells, as exceções são 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 solução fácil, que é substituir o uso inútil de catum 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 itera completamente sobre as linhas de entrada. Consulte Por que é while IFS= readusado 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 do espaço em branco à esquerda e à direita não importa, pois não deve haver nenhuma entrada de amostra, mas você pode precisar -revitar a expansão da barra invertida. É possível que while read lineseja de fato uma maneira correta de ler sua entrada (mas 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 para analisar. Verifique o que acontece se um dos valores das variáveis ​​contiver um caractere de nova linha, aspas duplas ou barra invertida.

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

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

Primeiro, você precisa de usar as aspas em torno da substituição de variáveis: echo "${kv#*=}"; caso contrário, o shell executa a divisão de palavras e a geração de nome de arquivo (ou seja, globbing) nele. Segundo, echonão é uma maneira confiável de imprimir uma string, porque algumas strings que começam com a -são tratadas como opções e algumas shells executam 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 um separadamente, o que está errado. Esta é corrigível, mas não há um método mais fácil, que é a utilização de instalações de manipulação de strings do shell: val=${kv#*=}; val=${val#\"}; val=${val%\"}.

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

while read name value; do
  value=${value#\"}; value=${value%\"}
  export name="$value"
done <"$1"
Gilles 'SO- parar de ser mau'
fonte
0

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

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

e depois:

. "$1"
Ciro Santilli adicionou uma nova foto
fonte
0

/ env

EXPORT=value
#NOTEXPORT=notvalue

roteiro

#!/bin/sh

for entry in $(cat /env)
do
  if [[ ! $entry == \#* ]]
  then
    export $entry
  fi
done
Łukasz Kurowski
fonte