AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 457273
Accepted
Øistein Søvik
Øistein Søvik
Asked: 2018-07-20 10:03:19 +0800 CST2018-07-20 10:03:19 +0800 CST 2018-07-20 10:03:19 +0800 CST

查找不正确的 YAML 标头

  • 772

我正在尝试确定我的项目中哪些文件的标头不正确。文件都是这样开始的

---
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 标头不正确,缺少作者。

grep yaml
  • 2 2 个回答
  • 322 Views

2 个回答

  • Voted
  1. Best Answer
    Jeff Schaller
    2018-07-21T13:28:16+08:002018-07-21T13:28:16+08:00

    这是一种方法。我假设您有 bash(通过文件递归循环)、sed 和 awk。除了使用 bash,您还可以使用findwith-exec来搜索文件。

    一般流程是:

    1. *.md递归地向 bash 询问文件列表
    2. 将每个文件传递sed给以提取 YAML 标头
    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 脚本中的正则表达式匹配模式调整为更严格或更宽松,具体取决于您的具体要求(也许您想要字母数字字符而不是“任何”,如.示例中的当前字符)。

    该sed语句通过以下方式提取 YAML 标头:

    • 抑制默认打印 ( -n)
    • 要求匹配模式的地址行:行首,---行尾;第二个模式必须出现在第一个模式之后。
    • p然后打印该地址范围

    该awk脚本有点过度构建,但为了清楚起见,我想将其拼写出来。每次调用 awk 时,它都会将三个标志变量设置为零或假。如果我们看到符合我们标准的行,我们将相应的标志设置为 one/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
  2. Kusalananda
    2022-08-28T11:11:42+08:002022-08-28T11:11:42+08:00

    要提取文件的标题,我们可以sed这样使用:

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

    这将删除文件中的所有内容,除了第 1 行和下一行之间的行---。第二个表达式还删除---了数据中的所有行,这样您就只剩下 YAML 标头了。

    我将使用来自 Andrey Kislyukyq的基于 Python 的实用程序。由于这是对多功能 JSON 解析器的方便包装,我们可以轻松检测对应于键的值是、非还是特定字符串等。jqnullnull

    在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 {} +
    

    或者,使用任何支持通配模式的 shell (使用in**启用):shopt -s globstarbash

    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选项。这使得实用程序的退出状态反映了最后计算的表达式的值,即在这种情况下,零表示true,非零表示false 。这使得直接在语句中使用我们的sed+管道变得容易。yqif

    使用我们的三个测试文件运行它,我们得到

    Invalid YAML header: ./file3.md
    
    • 0

相关问题

  • 来自 `service | 的意外结果 grep`

  • 读取带有单词的文本文件及其出现次数和排序的打印输出

  • 命令 ls | grep 只显示目录(当它也应该显示文件时)

  • grep 什么时候计数,什么时候不计数

  • grep --line-buffered 直到 X 行?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    ssh 无法协商:“找不到匹配的密码”,正在拒绝 cbc

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    如何卸载内核模块“nvidia-drm”?

    • 13 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Wong Jia Hau ssh-add 返回:“连接代理时出错:没有这样的文件或目录” 2018-08-24 23:28:13 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST
  • Martin Hope
    Bagas Sanjaya 为什么 Linux 使用 LF 作为换行符? 2017-12-20 05:48:21 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve