使用bash脚本将IANA反向文件转换为json文件

时间:2018-08-28 22:52:32

标签: regex bash shell sed

什么

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

那是因为json文件不能有重复的密钥。

示例:

该文件包含很多重复的链接,但在本示例中,我们使用以下两个:

LeftFront

我希望那些变成:

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

以便他们都输出多伦多。

到目前为止我已经尝试过:

到目前为止,我所做的正则表达式为this

  • 搜索:Link America/Toronto America/Montreal Link America/Toronto Canada/Eastern
  • 替换为:^Link[\s]*([a-zA-Z\/\-]*)[\s]*([a-zA-Z\/\-]*)$

最后,我尝试使用sed这样做,如下所示: "\2" : "\1",

但是由于某些原因,它会继续输出整个文件而不替换任何内容。

我在做什么错了?

3 个答案:

答案 0 :(得分:1)

我强烈建议使用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"

答案 1 :(得分: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

更好。

答案 2 :(得分:0)

解决方案:

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

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

它按预期工作,并创建JSON输出的正文

TL / DR:

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

运行他的命令后,我留下了一些不必要的行:

文件中包含的

A)注释

# This file is...

(这是通过告诉sed不要打印不匹配(发现here)的行来解决的,方法是在开头添加-n标志,并在{{1} }标记在脚本末尾) 和

B)一些未转换的行

p

(这是通过告诉Link Pacific/Pago_Pago Pacific/Samoa匹配组sed中不是空格的任何内容来解决的)

最后是整个脚本:

看起来像这样:

[^[:space:]]

并像这样运行脚本:#!/bin/bash echo "{"; sed -En 's|^Link[[:space:]]*([^[:space:]]*)[[:space:]]*([^[:space:]]*)$| "\2": "\1"|p' ./backward echo "}"; 输出一个漂亮的json文件。