如何阻止 SED 取消转义输出?
How to stop SED from un-escaping the output?
有一百万个与 sed 相关的问题,但我找不到这个具体案例。如果事实证明我是一个糟糕的 googler,我会很乐意接受纠正。
我有一个包含特殊字符和换行符的文件我们称它为query.kql:
Metrics
| where $__timeFilter(TimeGenerated)
| where ResourceProvider == "MICROSOFT.NETWORK"
| order by TimeGenerated asc
我还有一个 json 文件。它被称为 data.json:
{
"analytics": {
"query": "{{query.kql}}",
"resource": "$GlobalDataSource",
"resultFormat": "time_series"
}
}
我想做的是将 query.kql 的内容插入到 data.json 中的 {{query.kql}} 占位符中,以转义形式 (换行符->\n, " ->", 等等)
这给了我所需格式的 query.kql 的内容(有效):
q=$(sed -e "N;s/\n/\\n/" -e 's|["]|\"|g' query.kql)
#q: AzureMetrics\n| where $__timeFilter(TimeGenerated) | where ResourceProvider == \"MICROSOFT.NETWORK\"\n| order by TimeGenerated asc
我尝试过的:
# This does not work, because sed chokes on the result of the shell substitution:
sed -e "s/{{query.kql}}/$q/g" data.json
# Output: sed: -e expression #1, char 79: unterminated `s' command
# This works, but the output is wrong:
sed -e "s/{{query.kql}}/`echo $q`/g" data.json
# Output is unescaped and makes the json structure invalid:
"analytics": {
"query": "AzureMetrics
| where $__timeFilter(TimeGenerated) | where ResourceProvider == "MICROSOFT.NETWORK"
| order by TimeGenerated asc",
"resource": "$GlobalDataSource",
"resultFormat": "time_series"
},
我希望输出的是插入的 q 的确切内容:
{
"analytics": {
"query": "AzureMetrics\n| where $__timeFilter(TimeGenerated) | where ResourceProvider == \"MICROSOFT.NETWORK\"\n| order by TimeGenerated asc",
"resource": "$GlobalDataSource",
"resultFormat": "time_series"
}
}
如何让 sed 在输出中保持 $q 的原始内容?
我也乐于接受使用 awk、perl 或任何通常可从 bash 脚本获得的建议。
更新
原来我的主要问题是以正确转义的方式将文件内容读入 $q 变量。如果做得对,也不需要在第二个 sed 命令中使用 echo $q
。
我最终完成了这项工作:
# The first part escapes quotes and backslashes, the second part replaces the newlines by \n
query=$( sed -z 's#["\]#\\\&#g;s/\n/\\n/g' query.kql)
# I had to do some playing around before I found a suitable separator char, but turns out ~ does the trick in this specific case.
sed -i -e "s~{{query.kql}}~$query~g" $data.json
看起来你快到了。我想如果你尝试双重转义字符串你会得到你想要的。请尝试以下操作:
q=$(cat query.kql | sed -e ':a;N;$!ba;s/\n/\\n/g' -e 's#["]#\\"#g')
sed -e "s/{{query.kql}}/$q/g" data.json
这是我的输出:
{
"analytics": {
"query": "Metrics\n| where $__timeFilter(TimeGenerated)\n| where ResourceProvider == \"MICROSOFT.NETWORK\"\n| order by TimeGenerated asc",
"resource": "$GlobalDataSource",
"resultFormat": "time_series"
}
}
编辑: 顺便说一句,在转义任何其他内容之前,您还应该转义反斜杠“\”。否则,您最终可能会将原始反斜杠解释为最终结果中的转义。 sed -e 's/\/\\/g'
就在所有其他替换之前。
使用sed
$ q=$(sed '2s/|/\\n&/;s/"/\\&/g;4s/|/\\n&/' query.kql)
$ sed "s/{{query.kql}}/`echo $q`/" data.json
{
"analytics": {
"query": "AzureMetrics \n| where (TimeGenerated) | where ResourceProvider == \"MICROSOFT.NETWORK\" \n| order by TimeGenerated asc",
"resource": "",
"resultFormat": "time_series"
}
}
要处理 shell 中的 JSON 你应该使用 jq
:
jq --arg kql "$(< query.kql)" '.analytics.query = $kql' data.json
{
"analytics": {
"query": "Metrics\n| where $__timeFilter(TimeGenerated)\n| where ResourceProvider == \"MICROSOFT.NETWORK\"\n| order by TimeGenerated asc",
"resource": "$GlobalDataSource",
"resultFormat": "time_series"
}
}
更新
由于 OP 事先不知道 JSON 结构,因此最好使用其核心库中具有 JSON 编码器的语言。
- 与ruby;替换所有出现的
{{query.kql}}
:
ruby -rjson -pe 'BEGIN {kql = File.read("query.kql").to_json[1..-2]}; gsub("{{query.kql}}", kql)' < data.json
- 与
jq
;更新值为 "{{query.kql}}"
: 的所有键
jq --arg kql "$(< query.kql)" '.. |= if (. == "{{query.kql}}") then . = $kql else . end' data.json
这可能对你有用 (GNU sed):
sed '/{{\([^}]*\)}}/{
s//\n\n/
h
s/.*\n\(.*\)\n.*//
s/.*/cat "&"/e
s/\n/\n/g
s/"/\"/g
H
g
s/\n.*\n\(.*\)\n\(.*\)//
s/^/\n/
D}' file
确定包含要插入的文件的行,即 {{
和 }}
之间的字符串。
文件名用换行分隔。
复制该行。
删除文件名以外的所有内容。
用内容替换文件名。
转义换行符和双引号。
将修改后的文件内容字符串追加到原始行。
用修改后的内容替换文件名。
重复直到失败。
有一百万个与 sed 相关的问题,但我找不到这个具体案例。如果事实证明我是一个糟糕的 googler,我会很乐意接受纠正。
我有一个包含特殊字符和换行符的文件我们称它为query.kql:
Metrics
| where $__timeFilter(TimeGenerated)
| where ResourceProvider == "MICROSOFT.NETWORK"
| order by TimeGenerated asc
我还有一个 json 文件。它被称为 data.json:
{
"analytics": {
"query": "{{query.kql}}",
"resource": "$GlobalDataSource",
"resultFormat": "time_series"
}
}
我想做的是将 query.kql 的内容插入到 data.json 中的 {{query.kql}} 占位符中,以转义形式 (换行符->\n, " ->", 等等)
这给了我所需格式的 query.kql 的内容(有效):
q=$(sed -e "N;s/\n/\\n/" -e 's|["]|\"|g' query.kql)
#q: AzureMetrics\n| where $__timeFilter(TimeGenerated) | where ResourceProvider == \"MICROSOFT.NETWORK\"\n| order by TimeGenerated asc
我尝试过的:
# This does not work, because sed chokes on the result of the shell substitution:
sed -e "s/{{query.kql}}/$q/g" data.json
# Output: sed: -e expression #1, char 79: unterminated `s' command
# This works, but the output is wrong:
sed -e "s/{{query.kql}}/`echo $q`/g" data.json
# Output is unescaped and makes the json structure invalid:
"analytics": {
"query": "AzureMetrics
| where $__timeFilter(TimeGenerated) | where ResourceProvider == "MICROSOFT.NETWORK"
| order by TimeGenerated asc",
"resource": "$GlobalDataSource",
"resultFormat": "time_series"
},
我希望输出的是插入的 q 的确切内容:
{
"analytics": {
"query": "AzureMetrics\n| where $__timeFilter(TimeGenerated) | where ResourceProvider == \"MICROSOFT.NETWORK\"\n| order by TimeGenerated asc",
"resource": "$GlobalDataSource",
"resultFormat": "time_series"
}
}
如何让 sed 在输出中保持 $q 的原始内容? 我也乐于接受使用 awk、perl 或任何通常可从 bash 脚本获得的建议。
更新
原来我的主要问题是以正确转义的方式将文件内容读入 $q 变量。如果做得对,也不需要在第二个 sed 命令中使用 echo $q
。
我最终完成了这项工作:
# The first part escapes quotes and backslashes, the second part replaces the newlines by \n
query=$( sed -z 's#["\]#\\\&#g;s/\n/\\n/g' query.kql)
# I had to do some playing around before I found a suitable separator char, but turns out ~ does the trick in this specific case.
sed -i -e "s~{{query.kql}}~$query~g" $data.json
看起来你快到了。我想如果你尝试双重转义字符串你会得到你想要的。请尝试以下操作:
q=$(cat query.kql | sed -e ':a;N;$!ba;s/\n/\\n/g' -e 's#["]#\\"#g')
sed -e "s/{{query.kql}}/$q/g" data.json
这是我的输出:
{
"analytics": {
"query": "Metrics\n| where $__timeFilter(TimeGenerated)\n| where ResourceProvider == \"MICROSOFT.NETWORK\"\n| order by TimeGenerated asc",
"resource": "$GlobalDataSource",
"resultFormat": "time_series"
}
}
编辑: 顺便说一句,在转义任何其他内容之前,您还应该转义反斜杠“\”。否则,您最终可能会将原始反斜杠解释为最终结果中的转义。 sed -e 's/\/\\/g'
就在所有其他替换之前。
使用sed
$ q=$(sed '2s/|/\\n&/;s/"/\\&/g;4s/|/\\n&/' query.kql)
$ sed "s/{{query.kql}}/`echo $q`/" data.json
{
"analytics": {
"query": "AzureMetrics \n| where (TimeGenerated) | where ResourceProvider == \"MICROSOFT.NETWORK\" \n| order by TimeGenerated asc",
"resource": "",
"resultFormat": "time_series"
}
}
要处理 shell 中的 JSON 你应该使用 jq
:
jq --arg kql "$(< query.kql)" '.analytics.query = $kql' data.json
{
"analytics": {
"query": "Metrics\n| where $__timeFilter(TimeGenerated)\n| where ResourceProvider == \"MICROSOFT.NETWORK\"\n| order by TimeGenerated asc",
"resource": "$GlobalDataSource",
"resultFormat": "time_series"
}
}
更新
由于 OP 事先不知道 JSON 结构,因此最好使用其核心库中具有 JSON 编码器的语言。
- 与ruby;替换所有出现的
{{query.kql}}
:
ruby -rjson -pe 'BEGIN {kql = File.read("query.kql").to_json[1..-2]}; gsub("{{query.kql}}", kql)' < data.json
- 与
jq
;更新值为"{{query.kql}}"
: 的所有键
jq --arg kql "$(< query.kql)" '.. |= if (. == "{{query.kql}}") then . = $kql else . end' data.json
这可能对你有用 (GNU sed):
sed '/{{\([^}]*\)}}/{
s//\n\n/
h
s/.*\n\(.*\)\n.*//
s/.*/cat "&"/e
s/\n/\n/g
s/"/\"/g
H
g
s/\n.*\n\(.*\)\n\(.*\)//
s/^/\n/
D}' file
确定包含要插入的文件的行,即 {{
和 }}
之间的字符串。
文件名用换行分隔。
复制该行。
删除文件名以外的所有内容。
用内容替换文件名。
转义换行符和双引号。
将修改后的文件内容字符串追加到原始行。
用修改后的内容替换文件名。
重复直到失败。