这个问题有很多变体,但无论如何我想在这里问它,因为我非常接近(!)找到这个问题的解决方案 - 我只需要一点点推动。
设想
我有几个文本 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 在调用echo
and之前扩展变量和命令替换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