如何阻止 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

标识包含要插入的文件的行,即 之间的字符串。

用换行符分隔文件名。

复制该行。

删除文件名以外的所有内容。

将文件名替换为其内容。

转义换行符和双引号。

将修改后的文件内容的字符串追加到原始行。

用修改后的内容替换文件名。

重复直到失败。

【讨论】:

是 POSIX sed @Fravadona 不,它在评估 RHS 的替换命令中使用e 标志,即cat file

以上是关于如何阻止 SED 取消转义输出?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 unix 实用程序 (sed/tr/awk) 用非转义等效项替换所有转义序列

如何阻止 Django 转义 # 符号

sed命令反斜杠的转义

取消转义后如何转义嵌入的 JSON

如何阻止 MOYA/Alamfire 转义我的身体 json 参数?

如何阻止 Swift 在我的 [String] 中注入转义的单引号?