【问题标题】:How can I filter by a numeric field using jq?如何使用 jq 按数字字段过滤?
【发布时间】:2018-03-16 14:35:14
【问题描述】:

我正在编写一个脚本来查询 Bitbucket API 并删除从未下载过的 SNAPSHOT 工件。此脚本失败,因为它获取了所有快照工件,下载数量的选择似乎不起作用。

我的select 语句按下载次数过滤对象有什么问题?

当然,如果我可以使用过滤器查询 Bitbucket API,那么这里更直接的解决方案是。据我所知,API 不支持按下载过滤。

我的脚本是:

#!/usr/bin/env bash
curl -X GET --user "me:mykey" "https://api.bitbucket.org/2.0/repositories/myemployer/myproject/downloads?pagelen=100" > downloads.json

# get all values | reduce the set to just be name and downloads | select entries where downloads is zero | select entries where name contains SNAPSHOT | just get the name
#TODO i screwed up the selection somewhere its returning files that contain SNAPSHOT regardless of number of downloads
jq '.values | {name: .[].name, downloads: .[].downloads} | select(.downloads==0) | select(.name | contains("SNAPSHOT")) | .name' downloads.json > snapshots_without_any_downloads.js
#unique sort, not sure why jq gives me multiple values
sort -u snapshots_without_any_downloads.js | tr -d '"' > unique_snapshots_without_downloads.js

cat unique_snapshots_without_downloads.js | xargs -t -I % curl -Ss -X DELETE --user "me:mykey" "https://api.bitbucket.org/2.0/repositories/myemployer/myproject/downloads/%" > deleted_files.txt

来自 API 的原始输入的去识别样本是:

{
  "pagelen": 10,
  "size": 40,
  "values": [
    {
      "name": "myproject_1.1-SNAPSHOT_0210f77_mc_3.5.0.zip",
      "links": {
        "self": {
          "href": "https://api.bitbucket.org/2.0/repositories/myemployer/myproject/downloads/myproject_1.1-SNAPSHOT_0210f77_mc_3.5.0.zip"
        }
      },
      "downloads": 2,
      "created_on": "2018-03-15T17:50:00.157310+00:00",
      "user": {
        "username": "me",
        "display_name": "me",
        "type": "user",
        "uuid": "{3051ec5f-cc92-4bc3-b291-38189a490a89}",
        "links": {
          "self": {
            "href": "https://api.bitbucket.org/2.0/users/me"
          },
          "html": {
            "href": "https://bitbucket.org/me/"
          },
          "avatar": {
            "href": "https://bitbucket.org/account/me/avatar/32/"
          }
        }
      },
      "type": "download",
      "size": 430894
    },
    {
      "name": "myproject_1.1-SNAPSHOT_thanks_for_the_reminder_charles_duffy_mc_3.5.0.zip",
      "links": {
        "self": {
          "href": "https://api.bitbucket.org/2.0/repositories/myemployer/myproject/downloads/myproject_1.1-SNAPSHOT_0210f77_mc_3.5.0.zip"
        }
      },
      "downloads": 0,
      "created_on": "2018-03-15T17:50:00.157310+00:00",
      "user": {
        "username": "me",
        "display_name": "me",
        "type": "user",
        "uuid": "{3051ec5f-cc92-4bc3-b291-38189a490a89}",
        "links": {
          "self": {
            "href": "https://api.bitbucket.org/2.0/users/me"
          },
          "html": {
            "href": "https://bitbucket.org/me/"
          },
          "avatar": {
            "href": "https://bitbucket.org/account/me/avatar/32/"
          }
        }
      },
      "type": "download",
      "size": 430894
    },
    {
      "name": "myproject_1.0_mc_3.5.1.zip",
      "links": {
        "self": {
          "href": "https://api.bitbucket.org/2.0/repositories/myemployer/myproject/downloads/myproject_1.1-SNAPSHOT_0210f77_mc_3.5.1.zip"
        }
      },
      "downloads": 5,
      "created_on": "2018-03-15T17:49:14.885544+00:00",
      "user": {
        "username": "me",
        "display_name": "me",
        "type": "user",
        "uuid": "{3051ec5f-cc92-4bc3-b291-38189a490a89}",
        "links": {
          "self": {
            "href": "https://api.bitbucket.org/2.0/users/me"
          },
          "html": {
            "href": "https://bitbucket.org/me/"
          },
          "avatar": {
            "href": "https://bitbucket.org/account/me/avatar/32/"
          }
        }
      },
      "type": "download",
      "size": 430934
    }
  ],
  "page": 1,
  "next": "https://api.bitbucket.org/2.0/repositories/myemployer/myproject/downloads?pagelen=10&page=2"
}

我希望从这个 sn-p 得到的输出是 myproject_1.1-SNAPSHOT_thanks_for_the_reminder_charles_duffy_mc_3.5.0.zip - 该工件是一个 SNAPSHOT 并且下载量为零。

我已经使用这个中间步骤进行了一些调试:

jq '.values | {name: .[].name, downloads: .[].downloads} | select(.downloads>0) | select(.name | contains("SNAPSHOT")) | unique' downloads.json > snapshots_with_downloads.js
jq '.values | {name: .[].name, downloads: .[].downloads} | select(.downloads==0) | select(.name | contains("SNAPSHOT")) | .name' downloads.json > snapshots_without_any_downloads.js
#this returns the same values for each list!
diff unique_snapshots_with_downloads.js unique_snapshots_without_downloads.js

这个调整给出了一个更干净和独特的结构,它表明我不完全理解 jq 的某种拆分或流式处理:

#this returns a "unique" array like I expect, adding select to this still does not produce the desired outcome 
jq '.values | [{name: .[].name, downloads: .[].downloads}] | unique' downloads.json

这一步之后的数据是这样的。它只是从原始 API 响应中删除了我不需要的内容:

[
  {
    "name": "myproject_1.0_2400a51_mc_3.4.0.zip",
    "downloads": 0
  },
  {
    "name": "myproject_1.0_2400a51_mc_3.4.1.zip",
    "downloads": 2
  },
  {
    "name": "myproject_1.1-SNAPSHOT_391f4d5_mc_3.5.0.zip",
    "downloads": 0
  },
  {
    "name": "myproject_1.1-SNAPSHOT_391f4d5_mc_3.5.1.zip",
    "downloads": 2
  }
]

【问题讨论】:

  • A minimal reproducible example 将包含一些示例 JSON - 理想情况下,尽可能简单地举例说明您正在尝试做的事情,然后是尽可能短的 jq 代码尝试这样做事情。
  • 感谢您添加数据。也就是说,使用您的示例 JSON 运行 jq '.values | {name: .[].name, downloads: .[].downloads}' <test.json(截至问题的修订版 2),集合中没有 0s,因此 select(.downloads==0) 没有多大意义。
  • 顺便说一句,== "0" 需要一个字符串,而== 0 是一个整数。不知道为什么前者出现在代码中的任何地方。
  • "0" 与 0 比较麻烦。感谢您在这里坚持不懈,一些草率的调试步骤被复制到问题中。
  • Right -- 与-n 结合使用(因此第一个输入不会被吃掉作为初始上下文)。

标签: json bash select jq bitbucket-api


【解决方案1】:

据我了解:

  • 您想要全局唯一的输出
  • 您只需要带有downloads==0 的项目
  • 您只需要名称包含“SNAPSHOT”的项目

以下将完成:

jq -r '
[.values[] | {(.name): .downloads}]
| add
| to_entries[]
| select(.value == 0)
| .key | select(contains("SNAPSHOT"))'

这个版本没有使unique成为一个明确的步骤,而是从名称生成一个映射到下载计数器(add将这些值放在一起——这意味着在发生冲突的情况下,最后一个获胜),因此两者确保输出是唯一的。


鉴于您的测试 JSON,输出为:

myproject_1.1-SNAPSHOT_thanks_for_the_reminder_charles_duffy_mc_3.5.0.zip

应用于整体问题上下文,该策略可用于简化整体流程:

jq -r '[.values[] | {(.links.self.href): .downloads}] |  add | to_entries[] | select(.value == 0) | .key | select(contains("SNAPSHOT"))'

它通过作用于文件的 URL 而不仅仅是名称来简化整个过程。这简化了随后的 DELETE 调用。 sorttr 调用也可以删除。

【讨论】:

  • myproject_1.1-SNAPSHOT_0210f77_mc_3.5.0.zip 有 2 次下载。
  • 不是它的第二个副本。更仔细地查看您的输入文档。
  • 不需要标准化。我正在测试一些输入。我的调试和过于宽泛的示例以及我的编辑可能会搞砸我。
  • 哦,太聪明了!我不得不删除-n 标志。谢谢你。我了解了它是如何工作的,并且我学习了一些新的 jq 命令。
  • @CharlesDuffy - [{a:1},{a:2}]|add 不会产生 {"a":3}
【解决方案2】:

这是一个解决方案,它在根据下载总数进行选择之前对每个 .name.download 值求和:

reduce (.values[] | select(.name | contains("SNAPSHOT"))) as $v
  ({}; .[$v.name] += $v.downloads)
| with_entries(select(.value == 0))
| keys_unsorted[]

例子:

$ jq -r -f program.jq input.json
myproject_1.1-SNAPSHOT_thanks_for_the_reminder_charles_duffy_mc_3.5.0.zip

附言

我的 select 语句有什么问题...?

跳出来的问题是“选择”过滤器之前的管道位:

.values | {name: .[].name, downloads: .[].downloads} 

以这种方式使用.[] 会导致形成笛卡尔积——也就是说,上面的表达式将发出n*n JSON 集,其中n 是.values 的长度。你显然打算写:

.values[] | {name: .name, downloads: .downloads} 

可简写为:

.values[] | {name, downloads} 

【讨论】:

  • 感谢您对我原始代码的核心问题的补充说明。
猜你喜欢
  • 2021-08-18
  • 1970-01-01
  • 2014-08-30
  • 1970-01-01
  • 2021-07-04
  • 2011-08-31
  • 2018-06-05
  • 2016-06-15
  • 1970-01-01
相关资源
最近更新 更多