在文本文件中扩展具有同名环境变量的占位符?

在文本文件中扩展具有同名环境变量的占位符?

这个问题有很多变体,但无论如何我想在这里问它,因为我非常接近(!)找到这个问题的解决方案 - 我只需要一点点推动。

设想

我有几个文本 SQL 文件,用于在 Heroku 托管的 Postgres 数据库上运行报告。 SQL 文件之一当前如下所示:

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}
;

我会喜欢使用这样的过程:

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

这样我就可以将 SQL 通过管道传输到 Heroku 来生成报告,将所有变量替换为在运行脚本之前导出的相应 ENV 变量(这样我就可以简单地动态更改变量并运行其他报告,而不是编辑SQL 文件)。

我的问题

所以问题是我是几乎在那里,但我不太明白。当我这样使用时,我的表达方式sed有效:echo

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

但我无法让它处理cat整个文件内容:

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

sed使用with时我错过了什么吗cat?请帮忙!

更新:我使用的是 Mac OS X 10.8

答案1

zsh

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

对于任何类似 Bourne 的 shell(包括zsh):

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

两者都会展开所有`...`$(...)$((...))$[...]$xxx${xxx}因此,您需要转义不希望扩展的美元字符和反引号字符。

此外,对于第二个,文件的内容不得包含由三个字符组成的行EOF(这SQL应该不是问题)。

或者,你可以这样做:

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

您需要export这些变量才能perl了解它们。

至于为什么您的解决方案不起作用并且根本无法正常工作,您可以在之后运行它set -x以查看发生了什么:

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

通过使用双引号,shell 在调用echoand之前扩展变量和命令替换sed。所以,你实际上跑了:

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

sed无法执行代码。因此,即使您使用单引号,sed也不会扩展`...`.

答案2

我会让 Bash 为您插入值,而不是尝试编写正则表达式来执行相同的操作(或利用某些模板系统)。

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

其中 sql.sql 包含有效的 bash 字符串(因此将其用双引号引起来):

"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}
;"

答案3

如果你改变你的方法,改为这样做:

$ heroku pg:psql --app app_name < file.sql

相关内容