[輸出] jsonpath
===
###### tags: `K8s / commands`
###### tags: `Kubernetes`, `k8s`, `kubectl get`, `-o`, `output`, `jsonpath`, `jq`, `base64`, `decode`, `go-template`
<br>
[TOC]
<br>
## JSONPath 限制 (必讀)
```
-o jsonpath='{.data["\.dockerconfigjson"]}' -> X, `...` 視為純字串,不會被 SHELL 解析
-o jsonpath='{.data[".dockerconfigjson"]}' -> X, `...` 視為純字串,不會被 SHELL 解析
-o jsonpath='{.data[\"\.dockerconfigjson\"]}' -> X, `...` 視為純字串,不會被 SHELL 解析
-o jsonpath='{.data.\.dockerconfigjson}' -> O
-o jsonpath="{.data[\"\.dockerconfigjson\"]}" -> X, k8s 的 jsonpath 中的鍵值只能用單引號
-o jsonpath="{.data['.dockerconfigjson']}" -> X, . 是節點運算子 -> 結果:沒有錯誤,但也沒有輸出任何東西
-o jsonpath="{.data['\.dockerconfigjson']}" -> O
-o go-template='{{ index .data ".dockerconfigjson" }}'
```
- ### 上游程式:SHELL
- 使用單引號:字串內容不跳脫
- 使用雙引號:處理跳脫字元
- ### 下游程式:kubectl 採用的 JSONPath 解析器
- 只能用單引號`'`
- `.` 是節點運算子
- ### 正確使用範例
```
-o jsonpath="{.metadata.annotations['kubectl\.kubernetes\.io/last-applied-configuration']}"
-o jsonpath='{.metadata.annotations.kubectl\.kubernetes\.io/last-applied-configuration}'
```
<br>
### 備選:go-template 方式
用 go-template 的 index 函式(無須跳脫):
```bash
# 只取 raw 的 base64 值
kubectl get secret my-secret \
-o go-template='{{ index .data ".dockerconfigjson" }}'
# 再解碼
kubectl get secret my-secret \
-o go-template='{{ index .data ".dockerconfigjson" }}' \
| base64 -d
```
<br>
## 文件
- [JSONPath Support](https://kubernetes.io/docs/reference/kubectl/jsonpath/)
<br>
<hr>
<br>
## 常見範例
- ### 用 `[*]` 取全部 items:
```bash
kubectl get nodes -o jsonpath='{.items[*].kind}'
```
- ### 用 `range` 逐筆印(可加換行):
```bash
kubectl get nodes -o jsonpath='{range .items[*]}{.kind}{"\n"}{end}'
```
- ### 用 `?(<condition>)` 過濾器(filter),塞選 items:
```
pv_names=$(kubectl get -f "pvc.yaml" \
-o jsonpath='{.items[?(@.kind=="PersistentVolume")].metadata.name}' 2>/dev/null || true)
```
- `?` 是 **JSONPath 的「過濾器(filter)」語法**,意思是:**只保留符合條件的陣列元素**。
- `.items[...]`:`items` 是一個陣列(list)
- `[...]`:在陣列上做索引或篩選
- `?()`:**filter**,括號裡放條件,只保留符合條件的陣列元素
- `@`:代表「目前正在檢查的那個元素」(類似 foreach 裡的 item)
- `@.kind=="PersistentVolume"` 條件:該元素的 `kind` 欄位等於 `"PersistentVolume"`
- `...].metadata.name`:對篩選後留下的元素,取它們的 `metadata.name`
- `[?(@.kind=="PersistentVolume")]`
「在 `.items` 這個 list 裡,把 `kind` 是 `PersistentVolume` 的項目挑出來」
- **快速對照例子**:
假設 `.items` 有三個元素:
- `{"kind":"PersistentVolume","metadata":{"name":"pv-a"}}`
- `{"kind":"PersistentVolumeClaim","metadata":{"name":"pvc-a"}}`
- `{"kind":"Service","metadata":{"name":"svc-a"}}`
套上 filter 後只剩第一個,最後輸出就是:`pv-a`
<br>
<hr>
<br>
## 注意事項
### dot notation
```bash
kubectl get crd/images.genai.asus.com -o jsonpath='{.metadata.annotations}'
```
### bracket notation
> 如果鍵名中包含特殊字元(例如 `-` 或 `.`),則必須使用 bracket notation 並用引號括起來才能正確解析
- 底下會產生錯誤
```
kubectl get crd/images.genai.asus.com -o jsonpath='{.metadata["annotations"]}'
```
錯誤:
`error: error parsing jsonpath {.metadata["annotations"]}, invalid array index "annotations"`
- 正確使用方式:
```
kubectl get crd/images.genai.asus.com -o jsonpath="{.metadata['annotations']}"
```
- 比較差異
```
jsonpath='{.metadata.annotations}' --> O
jsonpath='{.metadata["annotations"]}' --> X (error)
jsonpath="{.metadata['annotations']}" --> O
```
### 鍵名包含特殊字元,無法使用 K8s jsonpath,需改用 jq
```bash
kubectl get crd/images.genai.asus.com \
-o jsonpath="{.metadata.annotations['controller-gen.kubebuilder.io/version']}"
```
- 輸出:沒有任何東西 -> K8s bug
- 注意:k8s 的 jsonpath 中的鍵值只能用**單引號**
- 使用 jq 改寫:
```
kubectl get crd/images.genai.asus.com \
-o json | jq '.metadata.annotations["controller-gen.kubebuilder.io/version"]' -r
```
- 輸出的值是一個 JSON text (JSON string)
- `-r`: raw data (output raw strings, not JSON texts)
- 沒有 `-r`,輸出 `"xxx"`
- 有 `-r`,輸出 `xxx` (不含雙引號)
- 注意:jq 的 jsonpath 中的鍵值只能用**雙引號**,否則會出現底下錯誤:
```
jq: error: syntax error, unexpected INVALID_CHARACTER (Unix shell quoting issues?) at <top-level>, line 1:
.metadata.annotations['controller-gen.kubebuilder.io/version']
jq: 1 compile error
```
<br>
---
## 討論
### 討論 jsonpath 的限制
- ### question
```
jsonpath='{.data["\.dockerconfigjson"]}' -> X
jsonpath='{.data[".dockerconfigjson"]}' -> X
jsonpath='{.data[\"\.dockerconfigjson\"]}' -> X
jsonpath="{.data[\"\.dockerconfigjson\"]}" -> X
jsonpath="{.data['\.dockerconfigjson']}" -> O
為何只有最後一個可行?
```
- ### gpt-o4-mini-high
### 1. Shell 解析
當你在 shell 下執行 `-o jsonpath=…`,最外層的引號會決定 shell 如何拆字串並處理跳脫字元。
- **單引號** (`'…'`):裡面所有字元都視為純文字,不做任何展開或替換。
- **雙引號** (`"…"`): 會先處理部分跳脫(如 `\"`、`\\`、`\$`),其他 `\.` 會保留給下游程式。
為了在裡面使用單引號包 key,又保留 `\.` 給 kubectl,最外層必須用雙引號包起來:
```bash
kubectl get secret my-secret \
-o jsonpath="{.data['\.dockerconfigjson']}"
```
### 2. kubectl JSONPath 規則
kubectl 採用的 JSONPath 解析器在 bracket-notation (`[…]`) 中 **只認單引號**,不支援雙引號:
- `{.data["foo"]}` → 錯誤:invalid array index `"foo"`
- `{.data['foo']}` → 正常取出 key = `foo`
因此必須用單引號將 key 包起來。
### 3. 跳脫「.」的必要性
你的 key 名稱是 `.dockerconfigjson`,前面那個「.」會被當成層級運算符號,必須用 `\.` 告訴 JSONPath 這個點是 key 名的一部分:
```bash
kubectl get secret my-secret \
-o jsonpath="{.data['\.dockerconfigjson']}" \
| base64 -d
```
### 小結
1. **最外層用雙引號**:讓 shell 不拆內部的 `'.…'`
2. **內部 bracket 用單引號**:kubectl JSONPath 只認單引號
3. **key 前加 `\.`**:避免把「.」當成路徑分隔
只有同時滿足這三點,shell 和 kubectl JSONPath 才能連續正確解析,成功印出 `.dockerconfigjson` 的內容。```
### [補充][小結第一點] **最外層用雙引號**:讓 shell 不拆內部的 `'.…'`
這句話可以這樣理解:
- 當你在 Shell 下輸入一段帶有引號的參數時,Shell 會先處理最外層的引號,決定哪一段文字是「同一個完整參數」。
- 如果你用 **單引號**(`'…'`)把整個字串包起來,那裡面就不允許再出現單引號 ── 一有單引號,Shell 就會認為那是字串結束,導致解析錯誤。
- 改用 **雙引號**(`"…"`)作為最外層,就可以在裡面自由放置單引號(`'.dockerconfigjson'`)而不用被 Shell 拆開或誤認結束,整段字串會原封不動地傳給 `kubectl`。
換句話說:
> **最外層用雙引號**,是為了告訴 Shell「這整段文字當成一個參數」,讓裡面的單引號也當作普通字元處理,不會被 Shell 拆分或當作字串結尾。
<br>
### 列出所有 CRD 名稱的 JSONPath 指令
- ### gpt-o3-high (2025/02/04)
如果想要列出所有 CRD(不加任何條件),可以使用以下 `kubectl get crd` 指令:
```
kubectl get crd -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}'
```
### 指令說明
- **`{range .items[*]}`**
遍歷輸出的 JSON 中 `.items` 陣列裡的所有項目。
- **`{.metadata.name}`**
針對每個項目,取出其 `metadata` 裡的 `name` 屬性,也就是 CRD 的名稱。
- **`{"\n"}`**
在每個名稱後面加上一個換行符號,讓輸出看起來更清晰。
- **`{end}`**
結束 `range` 的區塊。
這樣執行後,你就能看到所有 CRD 的名稱,每個名稱單獨一行。
### 參考範例
如果執行時,系統中有以下 CRD:
```
NAME
customresourcedefinitions.apiextensions.k8s.io
ingressroutes.traefik.containo.us
foo.custom.com
```
執行上述指令後,輸出將會是:
```
customresourcedefinitions.apiextensions.k8s.io
ingressroutes.traefik.containo.us
foo.custom.com
```
這樣,你就能快速獲取 Kubernetes 叢集中所有 CRD 的名稱。
<br>
### 查詢 Kubernetes CRD (Custom Resource Definitions)
- ### gpt-o3-high (2025/02/04)
在 Kubernetes 中,可以使用 `kubectl get crd` 來查看當前集群中的所有 CRD。如果我們想要篩選出特定 `group`,例如 `genai.example.com`,可以使用以下方法:
### 方法 1:使用 custom-columns 搭配 grep
此方法列出 CRD 的名稱與 group,然後過濾 `genai.example.com`:
```
kubectl get crd -o custom-columns=NAME:.metadata.name,GROUP:.spec.group | grep genai.example.com
```
### 方法 2:使用 JSON 輸出配合 jq
如果系統已安裝 [jq](https://stedolan.github.io/jq/),可以使用以下命令來篩選:
```
kubectl get crd -o json | jq -r '.items[] | select(.spec.group=="genai.example.com") | .metadata.name'
```
### 方法 3:使用 jsonpath(視 kubectl 版本而定)
可透過 jsonpath 來篩選 `group`,但某些 kubectl 版本可能會有不支援的情況:
```
kubectl get crd -o jsonpath='{range .items[?(@.spec.group=="genai.example.com")]}{.metadata.name}{"\n"}{end}'
```
## 結論
- 如果環境中有 `jq`,方法 2 是最可靠的。
- 若無 `jq`,方法 1 (custom-columns 搭配 `grep`) 也是簡單可行的解法。
- 方法 3 (jsonpath) 可能因 kubectl 版本不同而有所限制。
建議根據環境選擇最適合的方法來查詢 CRD!
<br>
### '{range .items[?(@.spec.group=="genai.example.com")]}{.metadata.name}{"\n"}{end}' 指令解讀
- ### gpt-o3-high (2025/02/04)
在 Kubernetes 中,可以使用 `kubectl get crd -o jsonpath` 來篩選特定的 CRD。以下是指令及其解讀:
### 指令:
```
kubectl get crd -o jsonpath='{range .items[?(@.spec.group=="genai.example.com")]}{.metadata.name}{"\n"}{end}'
```
### 指令解讀:
1. **`{range .items[?(@.spec.group=="genai.example.com")]} ... {end}`**
- **`.items`**:這是 Kubernetes 回傳的 JSON 中的主要陣列,包含所有 CRD 物件。
- **`[?(@.spec.group=="genai.example.com")]`**:這是一個篩選器(filter),`?()` 表示對陣列中的每個元素進行條件判斷,只有當 `.spec.group` 的值為 `"genai.example.com"` 時,該項目才會被選中。
- **`{range ...}{end}`**:這是一個迭代語法,對符合條件的每個項目執行括號內的操作。
2. **`{.metadata.name}`**
- 這部分會取出每個符合條件的 CRD 物件的 `.metadata.name`,即 CRD 的名稱。
3. **`{"\n"}`**
- 這部分會在每個名稱後加上換行符號,讓輸出結果每行顯示一個 CRD 名稱,方便閱讀。
### 指令範例輸出:
```
mycustomresource1.genai.example.com
mycustomresource2.genai.example.com
```
### 總結:
這個 JSONPath 指令的作用是:
- 找出 `spec.group` 為 `"genai.example.com"` 的 CRD;
- 對每個符合條件的 CRD,輸出其名稱並加上換行符號。
這樣就可以清楚地列出所有屬於 `genai.example.com` 群組的 CRD。
<br>
{%hackmd vaaMgNRPS4KGJDSFG0ZE0w %}