【发布时间】:2014-06-11 05:27:00
【问题描述】:
我需要你的帮助,因为我多年前曾与 AWK 一起工作,现在我的知识已经生疏了。尽管通过阅读一些指南让我的记忆有所恢复,但我确信我的代码包含一些错误。我在 SO 上阅读的大多数相关问题都涉及解析标准JSON,因此该建议不适用于我的情况。唯一接近我正在寻找的答案是这个 SO 问题的公认答案:using awk sed to parse update puppet file。但是我正在尝试实现两次解析,而我在那个答案中看不到它(或者理解不够)。
在考虑了其他选项(从R 本身到m4 以及介于两者之间的各种模板引擎)之后,我考虑过通过jsonlite 和stringr 包在R 中实现该解决方案,但这并不优雅。我决定编写一个简短的AWK 脚本来解析我的R 项目的数据收集配置文件,然后我的R 代码将读取它们。这样的文件大部分是JSON 文件,但有一些补充:
1) 它包含参数的嵌入变量,指的是同一文件中JSON元素的值(为简单起见,我决定将其放在根目录中JSON 树);
2) 参数通过在相应元素的名称之前放置一个星号 ('*') 来表示。
最初我计划了两种 嵌入变量的类型,您可以在此处看到它们 - internal(引用同一文件中的 JSON 元素,格式:${var})和外部(用户提供,格式:%{var})。但是,我仍然不清楚为外部参数传递值的机制和好处,所以目前我只专注于解析只包含内部变量的配置文件。所以,请暂时忽略外部变量。
示例配置文件:
{
"*source":"SourceForge",
"*action":"import",
"*schema":"sf0314",
"data":[
{
"indicatorName":"test1",
"indicatorDescription":"Test Indicator 1",
"indicatorType":"numeric",
"resultType":"numeric",
"requestSQL":"SELECT * FROM sf0305.users WHERE user_id < 100"
},
{
"indicatorName":"test2",
"indicatorDescription":"Test Indicator 2",
"indicatorType":"numeric",
"resultType":"numeric",
"requestSQL":"SELECT *
FROM sf1104.users a, sf1104.artifact b
WHERE a.user_id = b.submitted_by AND b.artifact_id = 304727"
},
{
"indicatorName":"totalProjects",
"indicatorDescription":"Total number of unique projects",
"indicatorType":"numeric",
"resultType":"numeric",
"requestSQL":"SELECT COUNT(DISTINCT group_id) FROM ${schema}.user_group"
},
{
"indicatorName":"totalDevs",
"indicatorDescription":"Total number of developers per project",
"indicatorType":"numeric",
"resultType":"data.frame",
"requestSQL":"SELECT COUNT(*) FROM ${schema}.user_group WHERE group_id = %{group_id}"
}
]
}
AWK 脚本:
#!/usr/bin/awk -f
BEGIN {
first_pass = true;
param = "\"\*[a-zA-Z^0-9]+?\"";
regex = "\$\{[a-zA-Z^0-9]+?\}";
params[""] = 0;
}
{
if (first_pass)
if (match($0, param)) {
print(substr($0, RSTART, RLENGTH));
params[param] = substr($0, RSTART, RLENGTH);
}
else
gsub(regex, params[regex], $0);
}
END {
if (first_pass) {
ARGC++;
ARGV[ARGIND++] = FILENAME;
first_pass = false;
nextfile;
}
}
任何帮助将不胜感激!谢谢!
更新(基于 G. Grothendieck 的回答)
以下代码(包装在一个函数中,对原始答案稍作修改)行为不正确,意外输出所有标记(带有“_”)配置键的值,而不仅仅是引用的:
generateConfig <- function(configTemplate, configFile) {
suppressPackageStartupMessages(suppressWarnings(library(tcltk)))
if (!require(gsubfn)) install.packages('gsubfn')
library(gsubfn)
regexKeyValue <- '"_([^"]*)":"([^"]*)"'
regexVariable <- "[$]{([[:alpha:]][[:alnum:].]*)}"
cfgTmpl <- readLines(configTemplate)
defns <- strapplyc(cfgTmpl, regexKeyValue, simplify = rbind)
dict <- setNames(defns[, 2], defns[, 1])
config <- gsubfn(regexVariable, dict, cfgTmpl)
writeLines(config, con = configFile)
}
函数调用如下:
if (updateNeeded()) {
<...>
generateConfig(SRDA_TEMPLATE, SRDA_CONFIG)
}
更新 2(根据 G. Grothendieck 的要求)
函数updateNeeded()检查两个文件的存在和修改时间,然后根据逻辑决定是否需要(重新)生成配置。文件(返回boolean)。
以下是模板配置文件(SRDA_TEMPLATE <- "./SourceForge.cfg.tmpl")的内容:
{
"_source":"SourceForge",
"_action":"import",
"_schema":"sf0314",
"data":[
{
"indicatorName":"test1",
"indicatorDescription":"Test Indicator 1",
"indicatorType":"numeric",
"resultType":"numeric",
"requestSQL":"SELECT * FROM sf0305.users WHERE user_id < 100"
},
{
"indicatorName":"test2",
"indicatorDescription":"Test Indicator 2",
"indicatorType":"numeric",
"resultType":"numeric",
"requestSQL":"SELECT *
FROM sf1104.users a, sf1104.artifact b
WHERE a.user_id = b.submitted_by AND b.artifact_id = 304727"
},
{
"indicatorName":"totalProjects",
"indicatorDescription":"Total number of unique projects",
"indicatorType":"numeric",
"resultType":"numeric",
"requestSQL":"SELECT COUNT(DISTINCT group_id) FROM ${schema}.user_group"
},
{
"indicatorName":"totalDevs",
"indicatorDescription":"Total number of developers per project",
"indicatorType":"numeric",
"resultType":"data.frame",
"requestSQL":"SELECT COUNT(*) FROM ${schema}.user_group WHERE group_id = 78745"
}
]
}
以下是自动生成的配置文件(SRDA_CONFIG <- "./SourceForge.cfg.json")的内容:
{
"_source":"SourceForge",
"_action":"import",
"_schema":"sf0314",
"data":[
{
"indicatorName":"test1",
"indicatorDescription":"Test Indicator 1",
"indicatorType":"numeric",
"resultType":"numeric",
"requestSQL":"SELECT * FROM sf0305.users WHERE user_id < 100"
},
{
"indicatorName":"test2",
"indicatorDescription":"Test Indicator 2",
"indicatorType":"numeric",
"resultType":"numeric",
"requestSQL":"SELECT *
FROM sf1104.users a, sf1104.artifact b
WHERE a.user_id = b.submitted_by AND b.artifact_id = 304727"
},
{
"indicatorName":"totalProjects",
"indicatorDescription":"Total number of unique projects",
"indicatorType":"numeric",
"resultType":"numeric",
"requestSQL":"SELECT COUNT(DISTINCT group_id) FROM SourceForge import sf0314.user_group"
},
{
"indicatorName":"totalDevs",
"indicatorDescription":"Total number of developers per project",
"indicatorType":"numeric",
"resultType":"data.frame",
"requestSQL":"SELECT COUNT(*) FROM SourceForge import sf0314.user_group WHERE group_id = 78745"
}
]
}
通知SourceForge 和import,在sf0314 之前意外填充。
非常感谢答案作者的帮助!
【问题讨论】:
-
这里的问题是什么?您可以控制输入配置文件的格式吗?
-
@EtanReisner:问题是我的 AWK 代码不起作用。相应地,问题是如何修复代码。是的,我控制着配置文件。无论我是否会采用 G. Grothendieck 提供的解决方案,我都乐意接受有关将我的代码修复为学习经验的建议。
-
[a-zA-Z^0-9]应该做什么?匹配数字、字母和^字符?当我运行您的 awk 脚本时,我在END部分收到四个关于不必要转义字符的警告和对nextfile的投诉。什么对你“不起作用”? -
所以在尝试让你的 awk 工作时,我发现了很多问题。您正在尝试使用正则表达式模式作为参数表中的键,但它们永远不会改变,因此根本不会得到您想要的。您的正则表达式本身需要调整(至少在此处),以便 awk 抛出关于它们的警告。您将变量名称设置为参数表中的值(因为您从不处理该行的右侧)。
-
^是方括号中的第一个字符时的补码范围。所以[0-9]表示匹配 0 到 9 之间的任何数字,而[^0-9]表示匹配 不是 0 到 9 之间的任何数字。
标签: json r parsing awk template-engine