Превратить обратный файл IANA в файл json с помощью скрипта bash - PullRequest
0 голосов
/ 29 августа 2018

Что

Я бы хотел превратить файл обратного часового пояса iana в файл 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

, но по какой-то причине он продолжает выводить весь файл, не подставляя ничего.

Что я делаю не так?

Ответы [ 3 ]

0 голосов
/ 29 августа 2018

Я предполагаю, что вы используете 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

еще лучше.

0 голосов
/ 29 августа 2018

Решение:

Ответом на мой вопрос решения является следующая команда:

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

Работает как положено и создает тело вывода JSON

TL / DR:

Именно ответ Рено заставил меня понять, что я должен использовать [[:space:]] вместо [/s].

После выполнения его команды у меня остались пара нежелательных строк:

A) комментариев, которые файл содержит вверху

т.е. # This file is...

(Это было решено путем указания sed не печатать строки, которые не совпадают (обнаружено, что здесь ) путем добавления флага -n в начале и флага p в конец сценария) и

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 > timezones.json выводит красивый файл json.

0 голосов
/ 29 августа 2018

Я настоятельно рекомендую использовать 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 (обернутая для вызова из оболочки):

# 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"
...