잘못된 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)
  • 다음 패턴과 일치하는 줄의 주소를 요청합니다. 줄 시작, ---줄 끝 두 번째 패턴은 첫 번째 패턴 뒤에 나타나야 합니다.
  • p그런 다음 해당 주소 범위를 인쇄하십시오.

스크립트 awk가 약간 과하게 구성되어 있지만 명확성을 위해 설명하겠습니다. awk가 호출될 때마다 세 개의 플래그 변수를 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과 다음 라인(일 경우) 사이를 제외하고 파일에서 모든 내용이 제거됩니다 ---. 두 번째 표현식은 또한 ---데이터의 모든 라인을 제거하므로 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값이 아닌 값이 필요한 키의 값이 올바른지 확인됩니다.

세 개의 샘플 파일에 대해 이 명령을 실행하고 스크립트 파일에서 테스트합니다 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옵션을 사용합니다. 이렇게 하면 유틸리티의 종료 상태가 마지막으로 평가된 표현식의 값인 0을 반영하게 됩니다.진짜, 그리고 0이 아닌잘못된이 경우. 이렇게 하면 명령문에서 sed+ 파이프를 직접 사용하는 것이 yq쉬워집니다 if.

우리가 얻은 세 가지 테스트 파일을 사용하여 이를 실행합니다.

Invalid YAML header: ./file3.md

관련 정보