我想将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",
但是由于某些原因,它会继续输出整个文件而不替换任何内容。
我在做什么错了?
答案 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输出的正文
特别是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文件。