[輸出] 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 %}