如何阻止 SED 取消转义输出?
Posted
技术标签:
【中文标题】如何阻止 SED 取消转义输出?【英文标题】:How to stop SED from un-escaping the output? 【发布时间】:2022-01-10 10:32:05 【问题描述】:有一百万个与 sed 相关的问题,但我找不到这个具体案例。如果事实证明我是一个糟糕的谷歌用户,我会很乐意接受纠正。
我有一个包含特殊字符和换行符的文件让我们称之为 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 的内容以转义形式(换行符->\n、“->”等)插入到 data.json 中的 query.kql 占位符中
这为我提供了所需格式的 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
【问题讨论】:
你真的需要用\n
替换换行符吗?
,1 使用双引号即可:q="$(sed ...)"
.2 你不知道`echo $q`
【参考方案1】:
要在 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 编码器的语言。
红宝石;替换所有出现的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
【讨论】:
谢谢,请记住这一点!在这种情况下不是最合适的,因为脚本在替换时不知道确切的 json 结构。 哦!我更新了我的答案以反映这一点;-)【参考方案2】:看起来你快到了。我认为如果你尝试双重转义字符串,你会得到你想要的。请尝试以下操作:
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'
就在所有其他替换之前。
【讨论】:
【参考方案3】:使用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"
【讨论】:
我认为在最后一个 RHS 中使用echo
没有意义,看起来 $q
应该足够了。
@WiktorStribiżew 确实如此。但是,OP 提到他/她已经尝试过,但对他/她不起作用。我使用了一个似乎对他们有用的解决方案。
输入字符串正确转义后,不再需要回显。谢谢!【参考方案4】:
这可能对你有用(GNU sed):
sed '/\([^]*\)/
s//\n\1\n/
h
s/.*\n\(.*\)\n.*/\1/
s/.*/cat "&"/e
s/\n/\\n/g
s/"/\\"/g
H
g
s/\n.*\n\(.*\)\n\(.*\)/\2\1/
s/^/\n/
D' file
标识包含要插入的文件的行,即 和
之间的字符串。
用换行符分隔文件名。
复制该行。
删除文件名以外的所有内容。
将文件名替换为其内容。
转义换行符和双引号。
将修改后的文件内容的字符串追加到原始行。
用修改后的内容替换文件名。
重复直到失败。
【讨论】:
是 POSIXsed
?
@Fravadona 不,它在评估 RHS 的替换命令中使用e
标志,即cat file
。以上是关于如何阻止 SED 取消转义输出?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 unix 实用程序 (sed/tr/awk) 用非转义等效项替换所有转义序列