
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
配列内の各要素の合計フィールドを抽出し、配列にオブジェクトとして追加します。age
persons
これはAndrey KislyukのPythonベースを使用しますyq
。https://kislyuk.github.io/yq/、多目的JSONパーサー用ラッパーですjq
。コマンドからこのオプションを削除すると、-y
JSON出力が表示されます。
Mike FarahのGoベースのyq
代替案を使用してください。
yq -N '[{ "name": .name, "age": .age }]' files | yq '{ "persons": . }'
シェルでは、次のように現在のディレクトリまたはその下のすべてのファイルbash
に出力ファイルを適用して、現在のディレクトリに出力ファイルを作成できます。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
が長すぎるコマンドに拡張されると仮定します。
最初にシェルオプションを有効にすると、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配列を収集し、それを最終出力のキー値として使用する最後のコマンドがあります。name
age
yq
persons
同様に、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.yaml
name:
age:
gsub
awk
gsub
stdout
通常、リダイレクトを使用して出力をファイルに書き込みますが、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
-
各項目の前にインデントが本当に必要な場合は、name
2番目のステップで追加できます。
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
注:このコードはテストされていません。