( 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"