無効なYAMLヘッダーを見つける

無効なYAMLヘッダーを見つける

私のプロジェクトのどのファイルに間違ったヘッダーがあるかを確認しようとしています。ファイルはすべて次のように始まります。

---
header:
.
.
.
title: 
some header:
.
.
.
more headers:
level: 
.
.
.
---

どこ。 。 。より多くのヘッダーを意味します。タイトルにインデントは含まれていません。次の式を使用すると、各ファイルからYAMLヘッダーを抽出できます。

grep -Przo --include=\*.md "^---(.|\n)*?---" .

今、私は間違ったYAMLヘッダーをリストしたいと思います。

  • 各YAMLヘッダーにtitle: some text
  • 各YAMLヘッダーには以下が必要です。language: [a-z]{2}
  • external: .*またはを含める必要がありますauthor: .*
  • title:level:、 の位置external:language:さまざまです。

私は次のようなことをしようとします。

grep -L --include=\*.md -e "external: .*" -e "author: .* ."

しかし、問題は、YAMLヘッダーだけでなくファイル全体を検索することです。したがって、上記の問題に対する解決策は、以前に検索したYAMLヘッダーの結果を再びgrepに供給する方法に依存していると思います。頑張った

grep -Przo --include=\*.md "^---(.|\n)*?---" . | xargs -0 grep "title:";

しかし、これを行うと、「該当するファイルやディレクトリがありません」というエラーが発生するため、どのように進むべきかわかりません。

例:

---
title: Rull-en-ball
level: 1
author: Transkribert og oversatt fra [Unity3D](http://unity3d.com)
translator: Bjørn Fjukstad
license: Oversatt fra [unity3d.com](https://unity3d.com/learn/tutorials/projects/roll-ball-tutorial)
language: nb
---

作成者、言語、タイトルを含むYAMLを編集してください。

---
title: Mini Golf
level: 2
language: en
external: http://appinventor.mit.edu/explore/ai2/minigolf.html
---

作成者の代わりにタイトル、言語、外部を使用してYAMLを編集してください。

---
title: 'Stjerner og galakser'
level: 2
logo: ../../assets/img/ccuk_logo.png
license: '[Code Club World Limited Terms of Service](https://github.com/CodeClub/scratch-curriculum/blob/master/LICENSE.md)'
translator: 'Ole Andreas Ramsdal'
language: nb
---

無効なYAMLヘッダー、著者の欠落。

答え1

これは一つの方法です。私はあなたがbash(再帰的にファイルを繰り返す)、sedとawkを持っていると仮定します。 bashを使用する代わりにfindwithを使用-execしてファイルを検索することもできます。

一般的なプロセスは次のとおりです。

  1. *.mdbashにファイルリストを再帰的に要求する
  2. 各ファイルを渡してsedYAMLヘッダーを抽出する
  3. 検証のために、このYAMLヘッダーをawkに渡してください。
  4. ヘッダーの検証に失敗した場合は、ファイル名を印刷します。

スクリプト:

#!/bin/bash
shopt -s globstar

for file in **/*.md
do
  # use sed for the header
  sed -n /^---$/,/^---$/p "$file" |
  awk '
        BEGIN {
                good_title=0
                good_lang=0
                good_extaut=0
        }
        /^title: .*/             { good_title=1  }
        /^language: [a-z][a-z]$/ { good_lang=1   }
        /^author: .*/            { good_extaut=1 }
        /^external: .*/          { good_extaut=1 }
        END {
                if (good_title && good_lang && good_extaut)
                        exit 0
                else
                        exit 1
        }
        '  \
  || printf "Incorrect header found in %s\n" "$file"
done

.特定の要件に応じて、awkスクリプトの正規表現一致パターンをより厳密または緩やかに簡単に調整できます(例の現在の文字のように、「any」ではなく英数字が必要になる場合があります)。

このsedステートメントは、次のようにYAMLヘッダーを抽出します。

  • デフォルト印刷を無効にする(-n
  • 次のパターンに一致する行のアドレスを要求します。行の始まり、行の終わり---2番目のパターンは最初のパターンの後に表示する必要があります。
  • p次に、対応するアドレス範囲を印刷します。

スクリプトawkは少し過度に構成されていますが、明確にするために説明します。 awkが呼び出されるたびに、3つのフラグ変数を0またはfalseに設定します。基準を満たす行がある場合は、そのフラグを1 / trueに設定します。すべての行が確認されると、これらのフラグの状態に基づいて成功または失敗を返します。検証を「通過」するには、すべてtrueでなければなりません。

適切に名前が付けられたサンプルファイルを現在のディレクトリとサブディレクトリに分散します。

$ tree .
.
├── bad1.md
├── good1.md
├── good2.md
└── subdir
    ├── bad1.md
    └── good1.md

1 directory, 5 files

...スクリプト出力:

Incorrect header found in bad1.md
Incorrect header found in subdir/bad1.md

答え2

ファイルヘッダーを抽出するには、sed次のように使用できます。

sed -e '1,/^---$/!d' -e '/^---$/d' filename

これにより、行1と次の行(ある場合)の間を除き、ファイルからすべての内容が削除されます---。 2番目の式は---データのすべての行も削除するため、YAMLヘッダーのみが残ります。

yqPythonベースのユーティリティを使いましょう。アンドレイ・キースリューク。これは多目的JSONパーサーのための便利なラッパーなので、jqキーに対応する値が特定の文字列かnullどうかを簡単に検出できます。null

jq構文では、キーがkeynameオブジェクトに存在するかどうかをテストできますhas("keyname")REキー値が特定の正規表現と一致することをテストすることもできます.keyname | test("RE")

質問に記載されているテストは、次のjq式に翻訳できます。

has("title")            and
(.title | test("."))    and
has("language")         and
(.language | test("[a-z]{2}"))  and
(has("external") or has("author"))

または、より短いですが、あまり表現的ではありません。

(.title? != null) and
(.language? | test("[a-z]{2}")) and
(has("external") or has("author"))

これにより、すべてのキーが存在し、値null以外の値を必要とするキーの値が正しいことが確認されます。

3つのサンプルファイルに対してこのコマンドを実行し、スクリプトファイルでテストしますvalidate

$ sed -e '1,/^---$/!d' -e '/^---$/d' file1.md | yq -f validate
true
$ sed -e '1,/^---$/!d' -e '/^---$/d' file2.md | yq -f validate
true
$ sed -e '1,/^---$/!d' -e '/^---$/d' file3.md | yq -f validate
false

.mdこれを一般化して、現在のディレクトリまたはその下のディレクトリ内のすべてのファイルをテストできます。find次のようになります。

find . -name '*.md' -type f -exec sh -c '
    for pathname do
        if ! sed -e "1,/^---\$/!d" -e "/^---\$/d" "$pathname" |
             yq -e -f validate >/dev/null
        then
            printf "Invalid YAML header: %s\n" "$pathname"
        fi
    done' sh {} +

または、**グローブモードをサポートするシェルを使用してください(shopt -s globstarinで有効bash)。

for pathname in ./**/*.md
do
    if ! sed -e '1,/^---$/!d' -e '/^---$/d' "$pathname" |
         yq -e -f validate >/dev/null
    then
        printf 'Invalid YAML header: %s\n' "$pathname"
    fi
done

ここではさらに出力を削除し、yq代わりにツールとその-eオプションを使用します。これにより、ユーティリティの終了ステータスは、最後に評価された式の値であるゼロを反映します。本物、そしてゼロ以外間違ったこの場合。これにより、ステートメントでsed+パイプを直接使用するのがyq簡単になりますif

私たちが得た3つのテストファイルを使ってそれを実行します。

Invalid YAML header: ./file3.md

関連情報