Expande os marcadores de lugar com ambiente de mesmo nome vars no arquivo de texto?

2

Há muitas variações nesta questão, mas eu queria perguntar aqui de qualquer maneira, porque estou tão perto (!) de encontrar uma solução para essa - eu só preciso de um empurrãozinho.

Cenário

Eu tenho vários arquivos SQL de texto que uso para executar relatórios em um banco de dados do Postgres hospedado com o Heroku. Um dos arquivos SQL atualmente se parece com isto:

SELECT
  (SELECT COUNT(*) FROM metrics WHERE(organization_id = o.id AND year = ${sql_year})) AS metrics,
  (SELECT COUNT(*) FROM my_metrics WHERE(organization_id = o.id)) AS quarterly_metrics,
  (SELECT COUNT(*) FROM quarterly_metrics WHERE(organization_id = o.id AND year = ${sql_year})) AS quarterly_metrics,
  (SELECT COUNT(*) FROM quarterly_text_metrics WHERE(organization_id = o.id AND year = ${sql_year})) AS quarterly_text_metrics,
  (SELECT COUNT(*) FROM budgets WHERE(organization_id = o.id AND year = ${sql_year})) AS budgets,
  (SELECT COUNT(*) FROM goals WHERE(goalable_type = 'Organization' AND goalable_id = o.id AND year = ${sql_year})) AS goals
FROM
  organizations AS o
WHERE
  o.id = ${sql_org_id}
;

Gostaria que gostasse de usar um processo como este:

export sql_org_id=1; export sql_year=2013
cat reports/my_report.sql | sed -e "s/\(\${[a-zA-Z_]*}\)/'eval "echo ''"'/" | heroku pg:psql

Para que eu possa canalizar o SQL para o Heroku para o relatório, substituindo todas as variáveis com seus vars ENV correspondentes que foram exportados antes de executar o script (para que eu possa simplesmente alterar as variáveis dinamicamente e executar relatórios adicionais em vez de editar o arquivo SQL).

Meu problema

Então, a questão é que eu estou quase lá, mas não consigo entender. A expressão sed que tenho funciona quando uso echo assim:

export sql_year=2013
echo "SELECT ${sql_year} AS year" | sed -e "s/\(\${[a-zA-Z_]*}\)/'eval "echo ''"'/"

Mas não consigo trabalhar com cat no conteúdo inteiro do arquivo:

export sql_org_id=1; export sql_year=2013
cat reports/my_report.sql | sed -e "s/\(\${[a-zA-Z_]*}\)/'eval "echo ''"'/"

Estou faltando alguma coisa quando uso sed com cat ? Por favor ajude!

UPDATE: estou no Mac OS X 10.8

    
por Vance Lucas 01.08.2013 / 01:08

3 respostas

1

com zsh :

print -r -- "${(e)$(<reports/my_report.sql)}"

Com qualquer shell parecido com o Bourne (incluindo zsh ):

eval "cat << EOF
$(cat reports/my_report.sql)
EOF"

Ambos expandirão todos os '...' , $(...) , $((...)) , $[...] , $xxx e ${xxx} . Então, você precisará escapar dos caracteres dólar e backtick que não deseja expandir.

Além disso, para o segundo, o conteúdo do arquivo não deve conter uma linha que consiste nos três caracteres EOF (que para SQL não deve ser um problema).

Como alternativa, você pode fazer:

perl -pe 's/\${(\w+)}/$ENV{$1}/g' reports/my_report.sql

Você precisará export das variáveis, para perl estar ciente delas.

Por que sua solução não funciona e não está nem perto de funcionar, você pode executá-la depois de set -x para ver o que está acontecendo:

$ echo "SELECT ${sql_year} AS year" | sed -e "s/\(\${[a-zA-Z_]*}\)/'eval "echo ''"'/"
+ echo 'SELECT 2013 AS year'
++ eval 'echo '\'''\'''
+++ echo ''
+ sed -e 's/\(${[a-zA-Z_]*}\)//'
SELECT 2013 AS year

Usando aspas duplas, o shell expande as variáveis e a substituição de comandos antes de chamar echo e sed . Então, você realmente correu:

echo 'SELECT 2013 AS year' | sed -e 's/\(${[a-zA-Z_]*}\)//'

sed não pode executar o código. Portanto, mesmo se você tivesse usado aspas simples, sed não teria expandido o '...' .

    
por 01.08.2013 / 08:49
1

Eu deixaria o Bash interpolar os valores para você em vez de tentar escrever uma expressão regular para fazer o mesmo (ou aproveitar algum sistema de templates).

echo $(eval echo $(cat sql.sql))

Em que sql.sql contém uma string bash válida (assim, coloque-a entre aspas duplas):

"SELECT
  (SELECT COUNT(*) FROM metrics WHERE(organization_id = o.id AND year = ${sql_year})) AS metrics,
  (SELECT COUNT(*) FROM my_metrics WHERE(organization_id = o.id)) AS quarterly_metrics,
  (SELECT COUNT(*) FROM quarterly_metrics WHERE(organization_id = o.id AND year = ${sql_year})) AS quarterly_metrics,
  (SELECT COUNT(*) FROM quarterly_text_metrics WHERE(organization_id = o.id AND year = ${sql_year})) AS quarterly_text_metrics,
  (SELECT COUNT(*) FROM budgets WHERE(organization_id = o.id AND year = ${sql_year})) AS budgets,
  (SELECT COUNT(*) FROM goals WHERE(goalable_type = 'Organization' AND goalable_id =     o.id AND year = ${sql_year})) AS goals
FROM
  organizations AS o
WHERE
  o.id = ${sql_org_id}
;"
    
por 01.08.2013 / 01:45
0

E se você mudar sua abordagem para que você faça isso:

$ heroku pg:psql --app app_name < file.sql
    
por 01.08.2013 / 01:24