【问题标题】:Output semicolon-separated string输出分号分隔的字符串
【发布时间】:2019-04-08 22:52:35
【问题描述】:

假设我们有这个文件:

{
  "persons": [
    {
      "friends": 4,
      "phoneNumber": 123456,
      "personID": 11111
    },
    {
      "friends": 2057,
      "phoneNumber": 432100,
      "personID": 22222
    },
    {
      "friends": 50,
      "phoneNumber": 147258,
      "personID": 55555
    }
  ]
}

我现在想将1111122222333334444455555 的电话号码提取为分号分隔的字符串:

123456;432100;;;147258

运行时

cat persons.txt | jq ".persons[] | select(.personID==<ID>) | .phoneNumber"

每个&lt;ID&gt; 一次,然后将结果与; 粘合在一起,这非常慢,因为它必须为每个 ID(以及我要提取的其他字段)重新加载文件。

在单个查询中连接它:

 cat persons.txt | jq "(.persons[] | select(.personID==11111) | .phoneNumber), (.persons[] | select(.personID==22222) | .phoneNumber), (.persons[] | select(.personID==33333) | .phoneNumber), (.persons[] | select(.personID==44444) | .phoneNumber), (.persons[] | select(.personID==55555) | .phoneNumber)"

这也有效,但它给出了

123456
432100
147258

所以我不知道缺少哪些字段以及我必须插入多少个;

【问题讨论】:

  • 数字之间是否需要一个分号?我在你的问题中看到你写了'123456;432100;;;147258' 为什么最后一个数字前面有3个分号?
  • 是的,因为我在寻找 1111122222333334444455555,但 3333344444 没有出现在文件中因此是无效的。如果我只有一个;,我无法保证正确的数字在正确的列中。
  • 这很恶心,不是你要问的,但它可能会激发一些人:. as $input | [11111, 22222, 33333, 44444, 55555][] | (. as $id | $input.persons | map(select(.personID == $id))) (jqplay)
  • 现在这就是你要问的,但更恶心:[. as $input | [11111, 22222, 33333, 44444, 55555][] | . as $id | $input.persons | map(select(.personID == $id)) | map(.phoneNumber) | join("")] | join(";") (jqplay)。你应该认真避免使用它,等待比我更了解jq 的人会清理这个烂摊子。
  • 解决方案一定要用jq吗?

标签: json bash export-to-csv jq


【解决方案1】:

使用 input.json 中的示例输入,并使用 jq 1.6(或带有 INDEX/2 的 jq),以下 jq 调用会产生所需的输出:

jq -r --argjson ids '[11111, 22222, 33333, 44444, 55555]' -f tossv.jq input.json 

假设 tossv.jq 包含程序:

INDEX(.persons[]; .personID) as $dict
| $ids
| map( $dict[tostring] | .phoneNumber)
| join(";")

程序说明

  1. INDEX/2 生成一个用作字典的 JSON 对象。由于 JSON 键必须是字符串,所以上面第 3 行中必须使用tostring

  2. 使用join(";") 时,null 值实际上变成了空字符串。

  3. 如果您的 jq 没有 INDEX/2,那么现在可能是升级的好时机。否则,您可以通过谷歌搜索来获取其定义:jq "def INDEX" builtin.jq

【讨论】:

  • 如果能成功,那就太优雅了!不幸的是,我没有机会将我的 jq 1.5 升级到 1.6...
  • 我查到了,我什至查了字典里的“snarf”的意思!
  • @Bowi - 那你为什么不复制粘贴def呢?
  • 你的意思是 def index($i): indices($i) | .[0]; 来自 github.com/stedolan/jq/blob/master/src/builtin.jq
  • @Bowi - 不,当然不是。一方面,jq 名称区分大小写。另一方面,INDEX/2 的数量为 2。
【解决方案2】:

不幸的是,我无法测试peak's answer 是否有效,因为我只有 jq 1.5。这是我昨天晚上想出的:

  • 对于每个分号,添加以下查询

    (\";\" as \$a | \$a)
    
  • 生成的命令(摘要):

     cat persons.txt | jq "(<1's phone number>), (\";\" as \$a | \$a), 
     (<2's phone number>), (\";\" as \$a | \$a), ..."
    
  • 生成的命令(具体):

    cat persons.txt | jq "(.persons[] | select(.personID==11111) | .phoneNumber), (\";\" as \$a | \$a), 
    (.persons[] | select(.personID==22222) | .phoneNumber), (\";\" as \$a | \$a), 
    (.persons[] | select(.personID==33333) | .phoneNumber), (\";\" as \$a | \$a), 
    (.persons[] | select(.personID==44444) | .phoneNumber), (\";\" as \$a | \$a), 
    (.persons[] | select(.personID==55555) | .phoneNumber)"
    
  • 结果:

    123456
    ";"
    432100
    ";"
    ";"
    ";"
    147258
    
  • 删除换行符和":

    <commandAsAbove> | tr --delete "\n\""
    
  • 结果:

    123456;432100;;;147258
    

不要误会我的意思,这比峰值的答案要丑得多,但昨天对我有用。

【讨论】:

  • 最后两部分可以通过jq 轻松完成:要删除换行符,首先将结果放入数组中,然后将它们与空字符串 ([ command ] | join("")) 连接。这将为您留下"1;2;;;3" 结果,您可以在其中使用-r“原始输出”标志删除引号。见this jqplay
  • 哦和 bash 明智的:1)通过将 jq 命令用单引号括起来让您的生活更轻松,这样您就不必转义内部的双引号和美元 2)更喜欢 jq command filecat file | jq command
  • 另外,E as $a | $a 在此处可以简化为 E
【解决方案3】:

没有jq解决方案:

for i in $(seq 11111 11111 55555)
do
  string=$(grep -B1 "$i" persons.txt | head -1 | sed 's/.* \(.*\),/\1/g')
  echo "$string;" >> output
done
cat output | tr -d '\n' | rev | cut -d';' -f2- | rev > tmp && mv tmp output

这个小脚本会产生你想要的结果,如果输入数据变化,你可以快速调整它

cat output
123456;432100;;;147258

【讨论】:

  • 我看对了吗——这在很大程度上依赖于每次都相同的 JSON 结构,对吗?因此,当一个条目混合在一起时(例如,电话号码后面的朋友),它会停止工作吗?
  • 是的,这假设每个 JSON“人”将按该行顺序拥有朋友、电话和 ID。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多