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

答え2

これを行う方法はいくつかありますが、最も簡単な方法はおそらくfind注文する。

まず、新しい配列構造を使用して出力ファイルを生成します。

echo "persons:" > newfile.yaml

次に、各項目を識別する必要があります。文書ターゲットディレクトリのファイル名と一致しますexample.yaml(と呼びます/home/user/yaml-files)。これはfindの基本的なユースケースであり、理解するのはとても簡単です。

find /home/user/yaml-files -type f -name example.yaml

find-exec一致するものが見つかったら、オプションを使用して-execdirシェルコマンドを実行する強力な組み込み関数があります。 while-execと同じ作業ディレクトリで実行されるのは、シェルコマンドが実行されるため、より安全なオプションです。find-execdir「中」一致が見つかったディレクトリ。単純化のために使用します-exec

example.yamlこれらのファイルから目的の行を検索してフォーマットを再指定し、結果を出力ファイルに追加する必要があります。

find /home/user/yaml-files -type f -name example.yaml -exec awk '$1 ~ /^name:|^age:/ {gsub(/name:/,"  - name:",$1); gsub(/age:/,"    age:",$1); print $0}' {} \; | tee -a newfile.yaml

ここに含まれるコマンドは、空白やその他の文字が前に入らず、またはで始まるすべての行をawk検索します。文字列の置換に便利な組み込み関数です。ここに一致する行を印刷する2つのフィルタがあります。example.yamlname:age:gsubawkgsubstdout

通常、リダイレクトを使用して出力をファイルに書き込みますが、find -execそうすると少し複雑になります。この場合、このteeコマンドは素晴らしいです。出力をコンソールだけでなくファイルにも表示します。この-aオプションtee追加そうしないと、毎回ファイルが上書きされ、最後にファイルを書き込んだ結果のみが残ります。

このソリューションは、私が知っている限り、あなたが遭遇するすべてのLinuxシステムに存在するいくつかのコマンドを使用します。特別な要件はなく、コードは移植性に非常に優れています。

答え3

特定の名前のファイルを探している場合は、example.yaml非常に簡単に見つけることができます。まず、新しいファイルを作成し、すべてのファイルから始めるか、すべてのファイルからpersons:始まるすべての行を追加します。name:age:example.yaml

printf 'persons:\n' > personsFile
find /target/directory -name example.yaml -exec grep -E '^(name|age):' {} + >> personsFile

-各項目の前にインデントが本当に必要な場合は、name2番目のステップで追加できます。

printf 'persons:\n' > personsFile
find /target/directory -name example.yaml -exec grep -E '^(name|age):' {} + >> personsFile
sed -i 's/^name/  - name/; s/^age/    age/' personsFile

しかし、実際にはYAMLのような構造化された形式を扱っているのであれば、これをハックするのではなく、専用のツールを見なければなりません。

答え4

man find xargs grep bash以下を読んで実行してください。

printf "%s\n" "persons:" >newfile
find . -type f -name '*.yaml' -print0 | \
    xargs -0 -r \
        grep -E --no-filename 'name:|age:' >>newfile

注:このコードはテストされていません。

関連情報