kubernetes叢集管理之通過jq來擷取屬性

周國通發表於2019-06-19

系列目錄

首先要宣告,這裡的jq並不是批前端框架裡的jquery,而是一個處理json的命令列工具.

jq工具相比yq,它更加成熟,功能也更加強大,主要表現在以下幾個方面

  • 支援遞迴查詢(我點對我們平時檢視檔案很方便)

  • 支援條件過濾

  • 支援控制語句

  • 支援陣列範圍索引

這個工具在macos和windows都提供線上包安裝服務,linux並沒有官方包服務,需要下載後放到usr/bin目錄下

下載地址

centos下也可以通過以下方式安裝

wget http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
rpm -ivh epel-release-latest-7.noarch.rpm
yum repolist

基本語法

基本語法格式如下

jq [options] <jq filter> [file...]

需要把檔案放在最後面,但是也無所謂,很多時候我們並不處理本地的json,而是通過標準輸入來處理其它來源型別的json

我本地存有一個叫作test.json的json檔案,我們可以通過jq . test.json檢視檔案裡的所有內容

{
  "json": [
    "rigid",
    "better for data interchange"
  ],
  "yaml": [
    "slim and flexible",
    "better for configuration"
  ],
  "object": {
    "key": "value",
    "array": [
      {
        "name": null
      },
      {
        "name": true
      },
      {
        "name": 24
      }
    ]
  },
  "paragraph": "Blank lines denote\nparagraph breaks\n",
  "content": "Or we\ncan auto\nconvert line breaks\nto save space"
}

jq輸出的json檔案預設都是格式化過的,方便我們檢視

我們仿照上一節,來獲取json陣列裡的所有值.

tylerzhou@DESKTOP-OE0CB8G:/mnt/d/test/jqjqtest$ jq .json test.json
[
  "rigid",
  "better for data interchange"
]

通過以下命令獲取array陣列

tylerzhou@DESKTOP-OE0CB8G:/mnt/d/test/jqjqtest$ jq .object.array test.json
[
  {
    "name": null
  },
  {
    "name": true
  },
  {
    "name": 24
  }
]

通過與上節jq類似的命令獲取array裡面的值

tylerzhou@DESKTOP-OE0CB8G:/mnt/d/test/jqjqtest$ jq .object.array[].name test.json
null
true
24

陣列也可以在中括號裡面指定索引(跟絕大多數程式語言一樣,從0開始)也與前面的yq類似,不同的是這裡可以使用[start:end]的方式來索引範圍

上面的例子我們這次不取全部,只取第0個和第一個

tylerzhou@DESKTOP-OE0CB8G:/mnt/d/test/jqjqtest$ jq .object.array[0:2] test.json
[
  {
    "name": null
  },
  {
    "name": true
  }
]

除了索引範圍以後,還可以索引不連續的物件,語法為[index1,index2,...]形式

例如,以下操作獲取第零個和第二個物件

tylerzhou@DESKTOP-OE0CB8G:/mnt/d/test/jqjqtest$ jq .object.array[0,2] test.json
{
  "name": null
}
{
  "name": 24
}

以上操作並索引並不一定按照從小到大的順序,比如以下也是可以的

tylerzhou@DESKTOP-OE0CB8G:/mnt/d/test/jqjqtest$ jq .object.array[1,0,2] test.json
{
  "name": true
}
{
  "name": null
}
{
  "name": 24
}

可空物件

有時候我們不確定一個物件是否存在,這時候我們可以在物件後面跟個問號?來表明不確定這個物件是否存在

比如jq .yml? test.json表明你不確定yml這個屬性在要查詢的物件中是否存在,如果這個物件是不存在的,則返回的值為null

實際上你會發現,上面不寫問號返回的值也是null,並不會報錯.在這裡體現的不明顯,但是對陣列型別的如果一個物件不存在寫為陣列形式就會報錯

比如jq .yml[] test.json就會出現jq: error (at test.json:25): Cannot iterate over null (null)錯誤

而使用jq .yml[]? test.json即便yml物件不存在也不會報錯

使用逗號,來取多個物件

假如我們想要同時取json和yaml這兩個物件,可以使用,把兩個命令分開,把它們的結果合在一塊

tylerzhou@DESKTOP-OE0CB8G:/mnt/d/test/jqjqtest$ jq .json,.yaml test.json
[
  "rigid",
  "better for data interchange"
]
[
  "slim and flexible",
  "better for configuration"
]

使用括號

上面的使用都逗號的命令裡我們已經使用兩條命令了,實際工作中可能有更加複雜的命令組合,這時候可以使用()來組織.在jq裡使用()和在其它程式語言裡使用相同.

使用管道符|

在jq裡管道符概念和linux上概念基本相同.可以把上一個命令的結果作為下一個命令的輸出.前面的.object.array也可以寫作管道模式.object|.array先用.object取object的所有物件,然後結果作為下級管道的輸入值,.array從管道結果裡取array陣列

tylerzhou@DESKTOP-OE0CB8G:/mnt/d/test/jqjqtest$ jq '.object|.array' test.json
[
  {
    "name": null
  },
  {
    "name": true
  },
  {
    "name": 24
  }
]

遞迴查詢子物件

在kubernetes 的jsonpath裡,可以使用..來查詢子物件,非常方便,惟一不好的是如果子物件包含陣列型別則顯示為Map[xxx]型別,並且展示的結果不再是json格式,可讀性不是特別好.比較遺憾的是,在jq裡並不支援..這種語法,而是更為複雜一些,需要使用管道符.

命令的格式如下

..|.物件名?

首先這裡使用了..表示從根物件開始取,然後使用管道符,後面之所以使用可空物件(物件後面帶問號)是因為它會在每一層都查詢,不使用可空物件的話找不到就會報錯.

假如我們並不知道array物件的上一層是什麼,但是知道這個物件存在.可以使用下面命令來獲取.

tylerzhou@DESKTOP-OE0CB8G:/mnt/d/test/jqjqtest$ jq '..|.array?' test.json
null
[
  {
    "name": null
  },
  {
    "name": true
  },
  {
    "name": 24
  }
]
null
null
null

這裡之所以會出現好多的null,因為jq會進入每一層物件裡面查詢,找不到就返回null.為了展示美觀,我們也可以做一些簡的處理

tylerzhou@DESKTOP-OE0CB8G:/mnt/d/test/jqjqtest$ jq '..|.array?' test.json|sed 's/null//g'

[
  {
    "name":
  },
  {
    "name": true
  },
  {
    "name": 24
  }
]

以上命令使用sed把null替換為空,這裡不夠優雅的地方在於把第一個name的值null也替換為空了.

這裡的命令外層加了引號,之所以要加引號是因為這個管道是jq管道,如果不加引號就會被shell識別為shell命令管道,從而導致錯誤.

過濾物件

我們使用示例來說明,使用以下命令獲取kubernetes叢集中的一個pod的conditions資訊

[centos@k8s-master ~]$ kubectl get pod consul-0 -ojson|jq '.status.conditions[]'
[
  {
    "lastProbeTime": null,
    "lastTransitionTime": "2019-05-14T07:19:28Z",
    "status": "True",
    "type": "Initialized"
  },
  {
    "lastProbeTime": null,
    "lastTransitionTime": "2019-05-14T07:25:06Z",
    "status": "True",
    "type": "Ready"
  },
  {
    "lastProbeTime": null,
    "lastTransitionTime": "2019-05-14T07:25:06Z",
    "status": "True",
    "type": "ContainersReady"
  },
  {
    "lastProbeTime": null,
    "lastTransitionTime": "2019-05-14T07:19:28Z",
    "status": "True",
    "type": "PodScheduled"
  }
]

現在我想要過濾typeReady的物件,可以使用以下命令

[centos@k8s-master ~]$ kubectl get pod consul-0 -ojson|jq '.status.conditions[]|select(.type=="Ready")'
{
  "lastProbeTime": null,
  "lastTransitionTime": "2019-05-14T07:25:06Z",
  "status": "True",
  "type": "Ready"
}

以上命令把status裡的conditions陣列物件做為輸入傳遞給管道下一級,然後使用select函式裡面傳入條件.

以上是完全匹配type屬性值為Ready,如果想要模糊匹配,則可以通過管道使用contains函式

[centos@k8s-master ~]$ kubectl get pod consul-0 -ojson|jq '.status.conditions[]|select(.type|contains("Ready"))'
{
  "lastProbeTime": null,
  "lastTransitionTime": "2019-05-14T07:25:06Z",
  "status": "True",
  "type": "Ready"
}
{
  "lastProbeTime": null,
  "lastTransitionTime": "2019-05-14T07:25:06Z",
  "status": "True",
  "type": "ContainersReady"
}

這樣,兩個type值包含Ready的全部被選擇出來了.

正則匹配

上面的過濾條件中,由於Ready是大寫的,因此不論是相等比較還是包含比較,都必須使用大寫,如果是小寫則無法匹配到.但是很多時候我們可能對大小寫是不清楚的,我們希望匹配的時候區分大小寫,這在jq裡也是可以做到的,我們使用match函式,match函式為正則匹配函式.接收一個陣列形式的引數,陣列的第一項為要匹配的內容,第二項為開關(比如g代表全域性開關,i則代表不區分大小寫)

我們把上面的示例改造如下

[centos@k8s-master ~]$ kubectl get pod consul-0 -ojson|jq '.status.conditions[]|select(.type|match(["ready","i"]))'
{
  "lastProbeTime": null,
  "lastTransitionTime": "2019-05-14T07:25:06Z",
  "status": "True",
  "type": "Ready"
}
{
  "lastProbeTime": null,
  "lastTransitionTime": "2019-05-14T07:25:06Z",
  "status": "True",
  "type": "ContainersReady"
}

這樣使用正規表示式我們就可以不區分大小寫匹配了.

以上只列出了工作中可能常用的功能,詳細功能可以檢視官方文件

相關文章