使用JSON文件中的键值对替换文本文件中的标记

时间:2017-01-01 20:16:20

标签: json bash shell dictionary jq

我正在尝试编写一个shell脚本,该脚本可以读取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

但是,我仍在试图弄清楚如何遍历密钥并返回数据。

2 个答案:

答案 0 :(得分:1)

整个事情可以在jq中非常简单(并且非常有效)地完成。

为了便于说明,假设我们已将Schema::create('users', function (Blueprint $table) { $table->increments('id'); $table->string('first_name'); $table->string('last_name'); $table->string('email')->unique(); $table->enum('role', ['coach', 'admin'])->default('coach'); $table->string('password'); $table->rememberToken(); $table->timestamps(); }); Schema::create('teams', function (Blueprint $table) { $table->increments('id'); $table->integer('user_id')->unsigned()->nullable(); $table->string('name')->unique(); $table->string('slug')->unique(); $table->timestamps(); $table->foreign('user_id')->references('id')->on('users'); }); Schema::create('rounds', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->timestamps(); }); Schema::create('round_team', function (Blueprint $table) { $table->increments('id'); $table->integer('round_id')->unsigned(); $table->integer('team_id')->unisigned(); $table->timestamp('date')->nullable(); $table->timestamps(); $table->foreign('round_id')->references('id')->on('rounds'); $table->foreign('team_id')->references('id')->on('teams'); }); 定义为问题中给出的字典对象,并将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))

当然还有许多其他方式可以显示字典和模板字符串。

答案 1 :(得分:0)

我假设您的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命令;我们使用进程替换来生成sed命令而不是实际文件:

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

使用jq进行一些处理,然后进行一些sed替换。

这是jq命令的作用:

  • 使用-r选项生成原始输出(无引号,实际标签而非\t
  • 使用to_entries函数将输入JSON对象转换为键值对数组,从而产生

    [
      {
        "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
    ...
    

现在,我们将它传递给sed,它执行三次替换:

  • s~^~s|%~ - 将s|%添加到每行的开头
  • s~\t~%|~ - 将标签替换为%|
  • s~$~|g~ - 将|g添加到每行的末尾

这为我们提供了一个sed文件,如下所示:

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

请注意,对于这些替换,我们使用~作为分隔符,对于我们生成的替换命令,我们使用| - 主要是为了避免遇到包含/的字符串的问题

如果此sed文件存储为commands.sed,则整个命令将对应于

sed -f commands.sed infile.txt

<强>说明

  • 如果你的shell不支持进程替换,你可以使用sed -f -从标准输入读取sed:

    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有关使用非可打印字符作为分隔符)或甚至执行其他替换以首先删除分隔字符并将它们放回最后(请参阅thisthis Q&amp; A)。

    < / LI>
  • 某些seds(例如在MacOS中找到的BSD sed)在替换模式中使用\t时遇到问题。如果是这种情况,则s~\t~%|~必须将s~'$'\t''~%|~替换为&#34;拼接&#34;选项卡字符,或者(如果shell不支持ANSI-C引用),即使使用s~'"$(printf '\t')"'~%|~