【问题标题】:Turn IANA backward file to a json file with bash script使用 bash 脚本将 IANA 后向文件转换为 json 文件
【发布时间】:2018-08-29 22:13:38
【问题描述】:

什么

我想将 iana backward timezones file 转换为具有唯一键的 json 文件,但为了做到这一点,我必须确保 键成为值反之亦然

这是因为 json 文件不能有重复的键。

示例:

该文件包含大量重复链接,但在本例中,我们使用这两个:

Link    America/Toronto     America/Montreal
Link    America/Toronto     Canada/Eastern

我希望那些变成:

"美国/蒙特利尔": "美国/多伦多", “加拿大/东部”:“美国/多伦多”,

这样他们都输出多伦多。

到目前为止我所做的尝试:

目前我做的正则表达式是this:

  • 搜索:^Link[\s]*([a-zA-Z\/\-]*)[\s]*([a-zA-Z\/\-]*)$
  • 替换为:"\2" : "\1",

最后我尝试用 sed 这样做: sed -E 's|^Link[\s]*([a-zA-Z\/\-]*)[\s]*([a-zA-Z\/\-]*)$|"\2" : "\1"|' ./backward

但由于某种原因,它一直输出整个文件而没有替换任何内容。

我做错了什么?

【问题讨论】:

  • 我认为您的命令几乎是正确的。那么对于正则表达式,如何从[\s] 修改为\s?这是sed -E 's|^Link\s*([a-zA-Z\/\-]*)\s*([a-zA-Z\/\-]*)$|"\2" : "\1"|' ./backward。如果这不是您想要的,我很抱歉。

标签: regex bash shell sed


【解决方案1】:

我假设您使用的是 GNU sed。您的问题来自 GNU 扩展正则表达式的特殊性,不幸的是,这些特殊性没有得到很好的记录。来自Regular-Expressions.info,例如:

速记 类\w\W\s\S 可以用来代替[[:alnum:]_][^[:alnum:]_][[:space:]][^[:space:]]。你可以使用这些 直接在正则表达式中,但不在括号表达式中。反斜杠 括号内的表达式始终是文字。

因此,您不能在 [...] 集合定义中使用 \s 速记来表示 [:space:]。正如Tanaike 所指出的,您不需要设置定义并且:

sed -E 's|^Link\s*([a-zA-Z\/\-]*)\s*([a-zA-Z\/\-]*)$|"\2" : "\1"|' ./backward

应该可以。如果出于任何原因,您想使用集合定义,

sed -E 's|^Link[[:space:]]*([a-zA-Z\/\-]*)[[:space:]]*([a-zA-Z\/\-]*)$|"\2" : "\1"|' ./backward

应该也可以。请注意:

 sed -E 's|^Link\s+([a-zA-Z\/\-]+)\s+([a-zA-Z\/\-]+)$|"\2" : "\1"|' ./backward

可能更好。并且:

 sed -E 's|^Link\s+([[:alpha:]/-]*)\s+([[:alpha:]/-]*)$|"\2" : "\1"|' ./backward

更好。

【讨论】:

  • 解决方案 1、3 和 4 不起作用。解决方案 2 是唯一有效的解决方案!我刚刚在 OSX 上尝试了上述所有命令
  • OP 在 MacOS 上,所以 GNU sed 似乎不太可能(除非他们明确安装它)。
  • @SudoPlz:很高兴看到至少有一种解决方案有效。如果您想使用 GNU 实用程序而不是默认的实用程序,您可以使用 MacPortsHomebrew 安装它们。我目前在 macOS High Sierra 上,我使用 MacPorts 的 GNU sed 端口。我展示的所有解决方案都在此环境下进行了测试。
  • 知道了,非常感谢@RenaudPacalet,但现在我想我会使用第二个,这样我就不必在外部安装任何东西了。
【解决方案2】:

我强烈建议使用jq,这是一个基于 JSON 构建的工具(因此,与 sed 不同,它无法生成不是有效 JSON 的输出,除非明确指示到)。

以下内容旨在提高可读性而不是简洁性:

input='
Link    America/Toronto     America/Montreal
Link    America/Toronto     Canada/Eastern
'

# -R == raw input; -n == don't consume input until directed by "input" or "inputs"
jq -Rn '
# start by creating an array of smaller arrays, one per line
[inputs
 | select((. | length) > 1)    ## ignore empty lines
 | split("[[:space:]]+"; "")   ## Split on runs of whitespace
 | select(.[0] == "Link")]     ## Ignore anywhere first column is not "Link"
# then combine those smaller arrays to create key/value pairs in one big object
| reduce .[] as $item ({}; .[$item[2]]=$item[1])
' <<<"$input"

...正确发出:

{
  "America/Montreal": "America/Toronto",
  "Canada/Eastern": "America/Toronto"
}

...正如您在https://jqplay.org/s/RBBKMUS2pv 看到的那样


或者,用 Python 编写的相同逻辑(包装以从 shell 调用):

# capture your Python code in a variable via a quoted heredoc
# this lets it be included in your shell script as a literal
link2json_py=$(cat <<'EOF'
import json, sys

data = {}
for line in sys.stdin:
    line = line.rstrip()
    columns = line.split()
    if len(columns) < 3:
        continue
    if columns[0] != 'Link':
        continue
    data[columns[1]] = columns[2]
json.dump(data, sys.stdout)
sys.stdout.write('\n')
EOF
)

# define a shell function wrapping that Python code
link2json() {
  python -c "$link2json_py" "$@"
}

# and call that shell function
link2json <<<"$input"

【讨论】:

  • 多么棒的答案。这看起来很有希望,但我希望能够在多个工作站中使用相同的命令,而无需安装任何 3rd 方工具
  • 我会考虑 Python,然后 -- 随处可用,内置 JSON 生成库。
  • 使用 Python 至少比依赖于 sed 的专有(特定于供应商)扩展更具可移植性。 :)
【解决方案3】:

解决方案:

我的问题的答案是以下命令:

sed -En 's|^Link[[:space:]]*([^[:space:]]*)[[:space:]]*([^[:space:]]*)$| "\2" : "\1"|p' ./backward

它按预期工作并创建 JSON 输出的主体

TL/DR:

特别是 Renaud 的回答让我意识到我必须使用 [[:space:]] 而不是 [/s]

运行他的命令后,我留下了几行不需要的行:

A)文件包含在顶部的cmets

# This file is...

(通过在脚本开头添加-n 标志和在脚本末尾添加p 标志来告诉sed 不打印不匹配的行(发现here)来解决这个问题) 和

B)一些未转换的行

Link Pacific/Pago_Pago Pacific/Samoa

(通过告诉sed 匹配[^[:space:]] 组中不是空格的任何内容来解决这个问题)

最后是整个脚本:

看起来像这样:

#!/bin/bash
echo "{";
sed -En 's|^Link[[:space:]]*([^[:space:]]*)[[:space:]]*([^[:space:]]*)$|    "\2": "\1"|p' ./backward
echo "}";

然后像这样运行脚本:sh index.sh &gt; timezones.json 输出一个漂亮的 json 文件。

【讨论】:

    猜你喜欢
    • 2020-03-10
    • 2017-03-30
    • 2020-01-03
    • 1970-01-01
    • 1970-01-01
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    • 2016-12-22
    相关资源
    最近更新 更多