使用 Bash 遍歷嵌套目錄並從 YAML 文件中提取某些欄位
我正在學習
bash
,我需要的是遍歷一個目錄(裡面有其他目錄)並找到所有名為example.yaml
.這些文件有幾個鍵值對(下面的範例):
name: Andre age: 13 address: street weight: 78kgs
我需要的是在某個目錄(必須包括嵌套目錄)中使用 bash 命令查找所有
example.yaml
文件,然後僅將名稱和年齡複製到新文件中。需要創建這個新文件,它看起來像這樣:persons: - name: Andre age: 13 - name: Joao age: 18 ...
我試圖做這樣的事情來解決這個問題
printf 'persons:\n' > output.yml for i in $(find ./ -name "example.yaml"); do name=$(yq '.name' $i) age=$(yq '.age' $i) // append $name and $age to output.yaml done
注意:這個答案的長度是由於至少有兩個主要的實用程序變體稱為
yq
,用於解析 YAML 數據,功能和表達式語法略有不同,我將兩者都介紹。我還研究了簡單地使用文件名萬用字元來查找所有文件並使用find
(當輸入文件太多時)。最後,我解決了評論中提出的其他問題。不要迭代
find
. 相反,請從find
using呼叫您的實用程序-exec
。我在這個答案中有一個進一步的例子。您還缺少對某些擴展的引用。也可以看看:
給定命令行上的一個或多個 YAML 文件,以下
yq
命令將創建 YAML 數據摘要文件:yq -y -s '{ persons: map({ name: .name, age: .age }) }' files
該命令將所有輸入讀入一個大數組(感謝
-s
或--slurp
),然後將其傳遞給map()
命令。該map()
命令提取數組中每個元素的name
和age
欄位,並將它們作為對象添加到persons
數組中。
yq
這使用了來自https://kislyuk.github.io/yq/的 Andrey Kislyuk 基於 Python 的,它是通用 JSON 解析器的包裝器jq
。如果您-y
從命令中刪除該選項,您將獲得 JSON 輸出。
yq
改用Mike Farah 的基於 Go 的:yq -N '[{ "name": .name, "age": .age }]' files | yq '{ "persons": . }'
在
bash
shell 中,您可以將其應用於example.yaml
目前目錄中的所有文件或它下面的任何位置,在目前目錄中創建輸出文件output.yaml
,如下所示:shopt -s globstar failglob yq -y -s '{ persons: map({ name: .name, age: .age }) }' ./**/example.yaml >output.yaml
或者,使用 Mike Farah 的
yq
:shopt -s globstar failglob yq -N '[{ "name": .name, "age": .age }]' ./**/example.yaml | yq '{ "persons": . }' >output.yaml
這假設
example.yaml
文件少於幾千個,否則命令行會擴展為太長的命令。首先
globstar
啟用 shell 選項以允許我們使用文件名通配模式,該模式在路徑名**
中匹配。如果沒有匹配的文件名,/
我們還啟用shell 選項以使整個命令正常失敗。failglob
測試:
$ tree . ├── dir1 │ └── example.yaml ├── example.yaml └── script-andrey └── script-mike 1 directory, 4 files
$ cat script-andrey shopt -s globstar failglob yq -y -s '{ persons: map({ name: .name, age: .age }) }' ./**/example.yaml >output.yaml
$ bash script-andrey $ cat output.yaml persons: - name: Joao age: 18 - name: Andre age: 13
也測試邁克
yq
:$ cat script-mike shopt -s globstar failglob yq -N '[{ "name": .name, "age": .age }]' ./**/example.yaml | yq '{ "persons": . }' >output.yaml
$ bash script-mike $ cat output.yaml persons: - name: Joao age: 18 - name: Andre age: 13
如果您有成千上萬個這樣的 YAML 輸入文件,那麼您可能希望
yq
更聰明地應用find
.這是使用安德烈的
yq
:find . -name example.yaml -type f \ -exec yq -y -s 'map({ name: .name, age: .age })' {} + | yq -y '{ persons: . }' >output.yaml
這將查找所有名稱為
example.yaml
. 這些是分批傳遞的,將從每個yq
中提取name
和age
欄位,創建一個數組。然後有一個最終yq
命令收集生成的 YAML 數組並將其作為persons
鍵的值放置在最終輸出中。同樣,對於邁克的
yq
:find . -name example.yaml -type f \ -exec yq -N '[{ "name": .name, "age": .age }]' {} + | yq '{ "persons": . }' >output.yaml
使用與上述相同的文件集進行測試:
$ rm output.yaml $ find . -name example.yaml -type f -exec yq -y -s 'map({ name: .name, age: .age })' {} + | yq -y '{ persons: . }' >output.yaml
$ cat output.yaml persons: - name: Andre age: 13 - name: Joao age: 18
(執行為 Mike 設計的命令會
yq
生成相同的輸出。)請注意,輸出的順序取決於
find
查找文件的順序。您是否要對例如
name
欄位上的輸出文件進行排序,然後以下內容將對文件進行就地排序(請注意,我不知道如何使用 Mike Farah 的 Go-based 來執行此操作yq
):yq -i -y '.persons |= sort_by(.name)' output.yaml
以相反的順序排序(就地):
yq -i -y '.persons |= (sort_by(.name) | reverse)' output.yaml
在評論中,使用者詢問是否可以將數據附加到現有文件。這個有可能。
下面的命令假定最後一件事
output.yaml
是persons
數組的末尾(以便該命令能夠向其中添加新的數組條目)。使用安德烈的
yq
:shopt -s globstar failglob yq -y -s 'map({ name: .name, age: .age })' ./**/example.yaml >>output.yaml
或者,與
find
,find . -name example.yaml -type f \ -exec yq -y -s 'map({ name: .name, age: .age })' {} + >>output.yaml
使用邁克的
yq
:shopt -s globstar failglob yq -N '[{ "name": .name, "age": .age }]' ./**/example.yaml >>output.yaml
或者,使用
find
:find . -name example.yaml -type f \ -exec yq -N '[{ "name": .name, "age": .age }]' {} + >>output.yaml