首页 > 解决方案 > 用 sed 中命令的输出替换模式

问题描述

假设我有一些文本文件(在这种情况下为 json):

{
  "data": [
    {
      "timestamp": 1577856103107
    },
    {
      "timestamp": 1577869991302
    }
  ]
}

我想用更易读的日期格式替换一个模式(在本例中为 UNIX 毫秒时间戳)。

我正在尝试这个:

$ sed -E 's/(.*)([0-9]{13})/echo "\1\\"$(date --date="@$((\2\/1000))" --iso-8601=seconds)\\""/e' example.json

{
  "data": [
    {
      timestamp: "2020-01-01T00:21:43-05:00"
    },
    {
      timestamp: "2020-01-01T04:13:11-05:00"
    }
  ]
}

这有点好,但我不明白为什么周围的引号timestamp会丢失。

此命令有效:

sed -E 's/(.*)"(timestamp)"(: )([0-9]{13})/echo "\1\\"\2\\"\3\\"$(date --date="@$((\4\/1000))" --iso-8601=seconds)"\\"/e' example.json

{
  "data": [
    {
      "timestamp": "2020-01-01T00:21:43-05:00"
    },
    {
      "timestamp": "2020-01-01T04:13:11-05:00"
    }
  ]
}

我也不明白为什么我需要双反斜杠来在这个 sed 命令的右侧\\输出双引号。"

有没有更好的方法(或工具)来解决这个问题?

我在sed (GNU sed) 4.8zsh 5.8 (x86_64-pc-linux-gnu)谢谢!

标签: bashsed

解决方案


有没有更好的方法(或工具)来解决这个问题?

用于操作jsonsed的东西非常粗糙。您无法使用正则表达式解析 json。我(强烈)建议使用 json 感知工具,例如jq.

jq '.data[].timestamp |= (. / 1000 | strftime("%Y-%m-%dT%H:%M:%SZ"))'

但我不明白为什么时间戳周围的引号会丢失。

这:

echo "\1\\"$(date --date="@$((\2\/1000))" --iso-8601=seconds)\\""

被“取代”为:

echo ""timestamp": \"$(date --date="@$((1577856103107/1000))" --iso-8601=seconds)\""
     ^^
                ^------------------------------------------------------------------^

然后传递给 shell 并根据 shell 规则重新评估引号。

匹配(.*)真的是什么都不做,只匹配你想替换的部分。您可以改为仅匹配要替换的部分:

sed '/"timestamp":/s/([0-9]{13})/echo "\\"$(date --date="@$((\1\/1000))" --iso-8601=seconds)\\""/e'

为什么我需要双反斜杠 \ 在此 sed 命令的右侧输出双引号 "。

首先\\被解释sed为 single \

$ echo a | sed 's/.*/single slash: \\/'
single slash: \

然后将命令的结果sed传递给 shell,所有的 shell 解析规则都会再次完成。


推荐阅读