Bashを使用して入れ子になったディレクトリを繰り返し、YAMLファイルから特定のフィールドを抽出します。

Bashを使用して入れ子になったディレクトリを繰り返し、YAMLファイルから特定のフィールドを抽出します。

bash私は必要なものがディレクトリ(それに他のディレクトリを含む)を繰り返し、名前があるディレクトリを探すことを学んでいますexample.yaml

これらのファイルには複数のKey-Valueペアがあります(以下の例)。

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

ベストアンサー1

注:この回答の長さは、YAMLデータを解析するために機能と式の構文がわずかに異なる2つ以上のユーティリティバリアントがあるために発生し、両方をカバーyqしました。また、すべてのファイルを見つけるために単にファイル名グロービングを使用する方法も検討しました。そして使用済みfind(入力ファイルが多すぎる場合) 最後に、コメントで他の質問に答えます。


出力を繰り返さないでくださいfind。代わりにを呼び出すfindユーティリティを使用してください-exec。この答えの下に例があります。一部の拡張への参照もありません。

また見なさい:


コマンドラインに1つ以上のYAMLファイルがある場合、次のyqコマンドはYAMLデータ要約ファイルを生成します。

yq -y -s '{ persons: map({ name: .name, age: .age }) }' files

このコマンドは、すべての入力を大きな配列(thanks-sまたは--slurp)で読み取ってからmap()コマンドに渡します。このmap()コマンドは、name配列内の各要素の合計フィールドを抽出し、配列にオブジェクトとして追加します。agepersons

これはAndrey KislyukのPythonベースを使用しますyqhttps://kislyuk.github.io/yq/、多目的JSONパーサー用ラッパーですjq。コマンドからこのオプションを削除すると、-yJSON出力が表示されます。

Mike FarahのGoベースのyq代替案を使用してください。

yq -N '[{ "name": .name, "age": .age }]' files | yq '{ "persons": . }'

シェルでは、次のように現在のディレクトリまたはその下のすべてのファイルbashに出力ファイルを適用して、現在のディレクトリに出力ファイルを作成できます。example.yamloutput.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が長すぎるコマンドに拡張されると仮定します。

最初にシェルオプションを有効にすると、pathnames内で一致するファイル名globbingパターンをglobstar使用できます。また、一致するファイル名がない場合は、コマンド全体が正常に失敗するようにシェルオプションを有効にします。**/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

これはAndreを使用していますyq

find . -name example.yaml -type f \
    -exec yq -y -s 'map({ name: .name, age: .age })' {} + |
yq -y '{ persons: . }' >output.yaml

それから名前がexample.yaml。データは一括して渡され、そのデータからフィールドが抽出され、配列がyq作成されます。次に、結果のYAML配列を収集し、それを最終出力のキー値として使用する最後のコマンドがあります。nameageyqpersons

同様に、Mikeは次のように言いました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ベースのコードを使用してこれを行う方法がわかりませんyq)。

yq -i -y '.persons |= sort_by(.name)' output.yaml

逆順に(内部)ソートするには、次の手順を実行します。

yq -i -y '.persons |= (sort_by(.name) | reverse)' output.yaml

コメントでは、ユーザーは既存のファイルにデータを追加できるかどうかを尋ねました。これは可能です。

以下のコマンドは、最後の項目が配列output.yamlの終わりであると仮定しますpersons(コマンドが配列に新しい配列項目を追加できるように)。

Andreの使用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

Mikeのものを使用してください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

おすすめ記事