2017-01-01 6 views
2

Я пытаюсь написать сценарий оболочки, который может читать строку json, декодировать его в массив и проецировать через массив и использовать ключ/значение для замены строк в другом файле.Замените теги в текстовом файле с помощью пар ключ-значение из файла JSON

Если бы это был PHP, я бы написал что-то вроде этого.

$array = json_decode($jsonString, true); 
foreach($array as $key => $value) 
{ 
    str_replace($key, $value, $rawString); 
} 

Мне нужно, чтобы оно было преобразовано в сценарий Bash. Вот пример строки JSON.

{ 
    "login": "lambda", 
    "id": 37398, 
    "avatar_url": "https://avatars.githubusercontent.com/u/37398?v=3", 
    "gravatar_id": "", 
    "url": "https://api.github.com/users/lambda", 
    "html_url": "https://github.com/lambda", 
    "followers_url": "https://api.github.com/users/lambda/followers", 
    "following_url": "https://api.github.com/users/lambda/following{/other_user}", 
    "gists_url": "https://api.github.com/users/lambda/gists{/gist_id}", 
    "starred_url": "https://api.github.com/users/lambda/starred{/owner}{/repo}", 
    "subscriptions_url": "https://api.github.com/users/lambda/subscriptions", 
    "organizations_url": "https://api.github.com/users/lambda/orgs", 
    "repos_url": "https://api.github.com/users/lambda/repos", 
    "events_url": "https://api.github.com/users/lambda/events{/privacy}", 
    "received_events_url": "https://api.github.com/users/lambda/received_events", 
    "type": "User", 
    "site_admin": false, 
    "name": "Brian Campbell", 
    "company": null, 
    "blog": null, 
    "location": null, 
    "email": null, 
    "hireable": null, 
    "bio": null, 
    "public_repos": 27, 
    "public_gists": 23, 
    "followers": 8, 
    "following": 2, 
    "created_at": "2008-11-30T21:03:27Z", 
    "updated_at": "2016-12-21T23:53:11Z" 
} 

Я этот файл,

Lamba login name is %login%, and avatar url is %avatar_url% 

Я использую JQ

jq -c '.[]' /tmp/json | while read i; do 
    echo $i 
done 

Это выводит только значение часть. Как мне прокрутить ключ, а также получить значение?

Кроме того, я обнаружил, что ключи строки JSon могут быть возвращены с помощью

jq 'keys' /tmp/params 

Однако, я все еще пытаюсь понять, как цикл через ключ и возвращает данные.

ответ

-1

Я предполагаю, что ваш JSON находится в infile.json и текст с тегами, которые нужно заменить в infile.txt.

Вот совершенно нечитаемый один вкладыш, который делает это:

$ sed -f <(jq -r 'to_entries[] | [.key, .value] | @tsv' < infile.json | sed 's~^~s|%~;s~\t~%|~;s~$~|g~') infile.txt 
Lamba login name is lambda, and avatar url is https://avatars.githubusercontent.com/u/37398?v=3 

Теперь, чтобы расшифровать то, что это делает. Во-первых, несколько разрывы строк для читаемости:

sed -f <(
    jq -r ' 
     to_entries[] | 
     [.key, .value] | 
     @tsv 
    ' < infile.json | 
    sed ' 
     s~^~s|%~ 
     s~\t~%|~ 
     s~$~|g~ 
    ' 
) infile.txt 

Мы в основном с помощью команды, которая патч в учитывающего ее инструкции из файла; вместо реального файла, мы используем замену процесса для создания SED команды:

jq -r 'to_entries[] | [.key, .value] | @tsv' < infile.json | 
sed 's~^~s|%~;s~\t~%|~;s~$~|g~' 

Некоторые обработки с JQ, а затем несколько SED замен.

Это то, что команда JQ делает:

  • Сформировать сырой вывод (без кавычек, фактические вкладок вместо \t) с опцией -r
  • Turn входной JSON объекта в массив кнопочного пар значений с функцией to_entries, что приводит к

    [ 
        { 
        "key": "login", 
        "value": "lambda" 
        }, 
        { 
        "key": "id", 
        "value": 37398 
        }, 
        ... 
    

    ]

  • Получить все элементы массива с []:

    { 
        "key": "login", 
        "value": "lambda" 
    } 
    { 
        "key": "id", 
        "value": 37398 
    } 
    ... 
    
  • Получить список массивов с ключом/значение в каждом использовании [.key, .value], в результате чего

    [ 
        "login", 
        "lambda" 
    ] 
    [ 
        "id", 
        37398 
    ] 
    ... 
    
  • Наконец, используйте @tsv фильтр для получения пар ключ-значение в виде списка, разделенного вкладкой:

    login lambda 
    id  37398 
    ... 
    

Теперь мы труба это СЭД, которая выполняет три замены:

  • s~^~s|%~ – добавить s|% в начале каждой строки
  • s~\t~%|~ – заменить вкладку с %|
  • s~$~|g~ – добавить |g к концу каждой строки

Это дает нам патч в файл, который выглядит следующим образом:

s|%login%|lambda|g 
s|%id%|37398|g 
s|%avatar_url%|https://avatars.githubusercontent.com/u/37398?v=3|g 

Обратите внимание, что для этих замен, мы использовали ~ в качестве разделителя, а для замещения команд мы сгенерированные мы использовали | – главным образом, чтобы избежать запуска в проблемы со строками, содержащие /.

Если СЭД файл были сохранены как commands.sed, общая команда будет соответствовать

sed -f commands.sed infile.txt 

Замечания

  • Если ваша оболочка не поддерживает замену процесса, вы может заставить sed читать со стандартного ввода, используя sed -f -:

    jq -r 'to_entries[] | [.key, .value] | @tsv' < infile.json | 
    sed 's~^~s|%~;s~\t~%|~;s~$~|g~' | 
    sed -f - infile.txt 
    
  • Если infile.json содержало | или ~, вы должны выбрать различные разделители для SED замен (см, например this answer об использовании непечатаемого символа в качестве разделителя) или даже выполнить дополнительные замены, чтобы избавиться от сначала разделите символы и верните их в конец (см. this и this Q & A).

  • Некоторые из них (например, BSD sed, найденные в MacOS) имеют проблемы с \t, которые используются в шаблоне для замены. Если это так, то команда s~\t~%|~ должна быть заменена на s~'$'\t''~%|~, чтобы «соединить» символ табуляции, или (если оболочка не поддерживает котировку ANSI-C) даже с s~'"$(printf '\t')"'~%|~.
1

Все это можно сделать довольно просто (и очень эффективно) в jq.

Для наглядности предположим, что мы определили dictionary быть объект словаря, данные в вопросе, и template быть шаблоном строки:

def dictionary: { ...... }; 

def template: 
    "Lamba login name is %login%, and avatar url is %avatar_url%"; 

Тогда требуемая интерполяция может быть выполнена следующим образом:

dictionary 
| reduce to_entries[] as $pair (template; gsub("%\($pair.key)%"; $pair.value)) 

выше производит:

"Lamba login name is lambda, and avatar url is https://avatars.githubusercontent.com/u/37398?v=3" 

Есть, конечно, много других способов, которыми могут быть представлены словарь и строка шаблона.

+0

А хорошо, это делает мое решение выглядеть довольно глупым ... –

+0

@BenjaminW. Не каждый имеет доступ к jq с поддержкой регулярных выражений, поэтому неплохо иметь какую-то альтернативу, даже если (как вы это заметили) у нее есть свои оговорки. Возможно, вам захочется документировать то, что, по моему мнению, является довольно надежным обходным путем - с использованием контрольного символа (например, control-A) в качестве разделителя sed. Он устойчив, по крайней мере, до такой степени, что JSON не позволяет использовать сырые контрольные символы в строках JSON. – peak

+0

Я думал о том, чтобы добавить это явно, но об этом спрашивали и отвечали раньше - я мог бы попробовать найти Q & A и ссылку на него. Вопрос о вашем решении: возможно ли иметь содержимое словаря и шаблона во внешних файлах? Очевидно, вы можете использовать шаблон 'jq -n 'def:« ... »; уменьшить ... '

 Смежные вопросы

  • Нет связанных вопросов^_^