用正则表达式重写 YAML frontmatter
Rewrite YAML frontmatter with regular expression
我想使用 Jekyll 将我的 WordPress 网站转换为 GitHub 上的静态网站。
我使用了一个插件,可以将我的 62 篇文章作为 Markdown 导出到 GitHub。我现在有这些帖子,每个文件的开头都有额外的前言。它看起来像这样:
---
ID: 51
post_title: Here's my post title
author: Frank Meeuwsen
post_date: 2014-07-03 22:10:11
post_excerpt: ""
layout: post
permalink: >
https://myurl.com/slug
published: true
sw_timestamp:
- "399956"
sw_open_thumbnail_url:
- >
https://myurl.com/wp-content/uploads/2014/08/Featured_image.jpg
sw_cache_timestamp:
- "408644"
swp_open_thumbnail_url:
- >
https://myurl.com/wp-content/uploads/2014/08/Featured_image.jpg
swp_open_graph_image_data:
- '["https://i0.wp.com/myurl.com/wp-content/uploads/2014/08/Featured_image.jpg?fit=800%2C400&ssl=1",800,400,false]'
swp_cache_timestamp:
- "410228"
---
这个块没有被 Jekyll 正确解析,而且我不需要所有这些 frontmatter。我想将每个文件的 frontmatter 转换为
---
ID: 51
post_title: Here's my post title
author: Frank Meeuwsen
post_date: 2014-07-03 22:10:11
layout: post
published: true
---
我想用正则表达式来做这个。但是我对正则表达式的了解并不多。在这个论坛和大量 Google 搜索的帮助下,我并没有取得太大进展。我知道如何找到完整的 frontmatter,但如何用上面指定的一部分替换它?
我可能必须分步执行此操作,但我不知道如何执行此操作。
我使用 Textwrangler 作为编辑器来进行搜索和替换。
编辑我的 post 因为我第一次误解了这个问题,我没能理解实际的 post 在同一个文件中,就在 ---
使用 egrep 和 GNU sed,而不是内置的 bash,相对容易:
# create a working copy
mv file file.old
# get only the fields you need from the frontmatter and redirect that to a new file
egrep '(---|ID|post_title|author|post_date|layout|published)' file.old > file
# get everything from the old file, but discard the frontmatter
cat file.old |gsed '/---/,/---/ d' >> file
# remove working copy
rm file.old
如果您想要一次性完成:
for i in `ls`; do mv $i $i.old; egrep '(---|ID|post_title|author|post_date|layout|published)' $i.old > $i; cat $.old |gsed '/---/,/---/ d' >> $i; rm $i.old; done
为了更好的衡量,以下是我作为第一回复所写的内容:
============================================= ==============
我认为你把这种方式搞得太复杂了。
一个简单的 egrep 将做你想做的事:
egrep '(---|ID|post_title|author|post_date|layout|published)' file
重定向到新文件:
egrep '(---|ID|post_title|author|post_date|layout|published)' file > newfile
一次整个目录:
for i in `ls`; do egrep '(---|ID|post_title|author|post_date|layout|published)' $i > $i.new; done
在像您这样的情况下,最好使用实际的 YAML 解析器和一些脚本语言。将每个文件的元数据截断为独立文件(或字符串),然后使用 YAML 库加载元数据。加载元数据后,您可以毫无问题地安全地修改它们。然后使用同一个库中的序列化方法创建一个新的元数据文件,最后将这些文件重新组合在一起。
像这样:
<?php
list ($before, $metadata, $after) = preg_split("/\n----*\n/ms", file_get_contents($argv[1]));
$yaml = yaml_parse($metadata);
$yaml_copy = [];
foreach ($yaml as $k => $v) {
// copy the data you wish to preserve to $yaml_copy
if (...) {
$yaml_copy[$k] = $yaml[$k];
}
}
file_put_contents('new/'.$argv[1], $before."\n---\n".yaml_emit($yaml_copy)."\n---\n".$after);
(这只是一个未经测试的草稿,没有错误检查。)
你可以像这样用 gawk 来做:
gawk 'BEGIN {RS="---"; FS="[=10=]0" } (FNR == 2) { print "---"; split(, fm, "\n"); for (line in fm) { if ( fm[line] ~ /^(ID|post_title|author|post_date|layout|published):/) {print fm[line]} } print "---" } (FNR > 2) {print}' post1.html > post1_without_frontmatter_fields.html
您基本上是想编辑文件。这就是 sed(流编辑器)的用途。
sed -e s/^ID:(*)$^post_title:()$^作者:( )$^postdate:()$^layout:()$^published:()$ /ID:\npost_title:\nauthor:\npostdate:\nlayout:\npublished:\6/g
YAML(以及其他相对自由的格式,如HTML、JSON、XML)最好不要使用正则表达式进行转换,很容易为一个例子工作并打破下一个有额外的空格、不同的缩进等。
在这种情况下使用 YAML 解析器并非易事,因为许多人要么期望文件中有一个 YAML 文档(并且 barf 在 Markdown 部分是无关的东西),要么期望文件中有多个 YAML 文档(并且 barf 因为Markdown 不是 YAML)。此外,大多数 YAML 解析器会丢弃有用的东西,例如注释和重新排序映射键。
我多年来一直为我的 ToDo 项目使用类似的格式(YAML header,然后是 reStructuredText),并使用一个小的 Python 程序来提取和更新这些文件。给出这样的输入:
---
ID: 51 # one of the key/values to preserve
post_title: Here's my post title
author: Frank Meeuwsen
post_date: 2014-07-03 22:10:11
post_excerpt: ""
layout: post
permalink: >
https://myurl.com/slug
published: true
sw_timestamp:
- "399956"
sw_open_thumbnail_url:
- >
https://myurl.com/wp-content/uploads/2014/08/Featured_image.jpg
sw_cache_timestamp:
- "408644"
swp_open_thumbnail_url:
- >
https://myurl.com/wp-content/uploads/2014/08/Featured_image.jpg
swp_open_graph_image_data:
- '["https://i0.wp.com/myurl.com/wp-content/uploads/2014/08/Featured_image.jpg?fit=800%2C400&ssl=1",800,400,false]'
swp_cache_timestamp:
- "410228"
---
additional stuff that is not YAML
and more
and more
这个程序 ¹:
import sys
import ruamel.yaml
from pathlib import Path
def extract(file_name, position=0):
doc_nr = 0
if not isinstance(file_name, Path):
file_name = Path(file_name)
yaml_str = ""
with file_name.open() as fp:
for line_nr, line in enumerate(fp):
if line.startswith('---'):
if line_nr == 0: # don't count --- on first line as next document
continue
else:
doc_nr += 1
if position == doc_nr:
yaml_str += line
return ruamel.yaml.round_trip_load(yaml_str, preserve_quotes=True)
def reinsert(ofp, file_name, data, position=0):
doc_nr = 0
inserted = False
if not isinstance(file_name, Path):
file_name = Path(file_name)
with file_name.open() as fp:
for line_nr, line in enumerate(fp):
if line.startswith('---'):
if line_nr == 0:
ofp.write(line)
continue
else:
doc_nr += 1
if position == doc_nr:
if inserted:
continue
ruamel.yaml.round_trip_dump(data, ofp)
inserted = True
continue
ofp.write(line)
data = extract('input.yaml')
for k in list(data.keys()):
if k not in ['ID', 'post_title', 'author', 'post_date', 'layout', 'published']:
del data[k]
reinsert(sys.stdout, 'input.yaml', data)
你得到这个输出:
---
ID: 51 # one of the key/values to preserve
post_title: Here's my post title
author: Frank Meeuwsen
post_date: 2014-07-03 22:10:11
layout: post
published: true
---
additional stuff that is not YAML
and more
and more
请注意 ID
行的注释已妥善保留。
¹ 这是使用 ruamel.yaml YAML 1.2 解析器完成的,它试图在 round-trips 上保留尽可能多的信息,我是作者。
您也可以使用 python-frontmatter
:
import frontmatter
import io
from os.path import basename, splitext
import glob
# Where are the files to modify
path = "*.markdown"
# Loop through all files
for fname in glob.glob(path):
with io.open(fname, 'r') as f:
# Parse file's front matter
post = frontmatter.load(f)
for k in post.metadata:
if k not in ['ID', 'post_title', 'author', 'post_date', 'layout', 'published']:
del post[k]
# Save the modified file
newfile = io.open(fname, 'w', encoding='utf8')
frontmatter.dump(post, newfile)
newfile.close()
如果您想查看更多示例,请访问 this page
希望对您有所帮助。
我想使用 Jekyll 将我的 WordPress 网站转换为 GitHub 上的静态网站。
我使用了一个插件,可以将我的 62 篇文章作为 Markdown 导出到 GitHub。我现在有这些帖子,每个文件的开头都有额外的前言。它看起来像这样:
---
ID: 51
post_title: Here's my post title
author: Frank Meeuwsen
post_date: 2014-07-03 22:10:11
post_excerpt: ""
layout: post
permalink: >
https://myurl.com/slug
published: true
sw_timestamp:
- "399956"
sw_open_thumbnail_url:
- >
https://myurl.com/wp-content/uploads/2014/08/Featured_image.jpg
sw_cache_timestamp:
- "408644"
swp_open_thumbnail_url:
- >
https://myurl.com/wp-content/uploads/2014/08/Featured_image.jpg
swp_open_graph_image_data:
- '["https://i0.wp.com/myurl.com/wp-content/uploads/2014/08/Featured_image.jpg?fit=800%2C400&ssl=1",800,400,false]'
swp_cache_timestamp:
- "410228"
---
这个块没有被 Jekyll 正确解析,而且我不需要所有这些 frontmatter。我想将每个文件的 frontmatter 转换为
---
ID: 51
post_title: Here's my post title
author: Frank Meeuwsen
post_date: 2014-07-03 22:10:11
layout: post
published: true
---
我想用正则表达式来做这个。但是我对正则表达式的了解并不多。在这个论坛和大量 Google 搜索的帮助下,我并没有取得太大进展。我知道如何找到完整的 frontmatter,但如何用上面指定的一部分替换它?
我可能必须分步执行此操作,但我不知道如何执行此操作。
我使用 Textwrangler 作为编辑器来进行搜索和替换。
编辑我的 post 因为我第一次误解了这个问题,我没能理解实际的 post 在同一个文件中,就在 ---
使用 egrep 和 GNU sed,而不是内置的 bash,相对容易:
# create a working copy
mv file file.old
# get only the fields you need from the frontmatter and redirect that to a new file
egrep '(---|ID|post_title|author|post_date|layout|published)' file.old > file
# get everything from the old file, but discard the frontmatter
cat file.old |gsed '/---/,/---/ d' >> file
# remove working copy
rm file.old
如果您想要一次性完成:
for i in `ls`; do mv $i $i.old; egrep '(---|ID|post_title|author|post_date|layout|published)' $i.old > $i; cat $.old |gsed '/---/,/---/ d' >> $i; rm $i.old; done
为了更好的衡量,以下是我作为第一回复所写的内容:
============================================= ==============
我认为你把这种方式搞得太复杂了。
一个简单的 egrep 将做你想做的事:
egrep '(---|ID|post_title|author|post_date|layout|published)' file
重定向到新文件:
egrep '(---|ID|post_title|author|post_date|layout|published)' file > newfile
一次整个目录:
for i in `ls`; do egrep '(---|ID|post_title|author|post_date|layout|published)' $i > $i.new; done
在像您这样的情况下,最好使用实际的 YAML 解析器和一些脚本语言。将每个文件的元数据截断为独立文件(或字符串),然后使用 YAML 库加载元数据。加载元数据后,您可以毫无问题地安全地修改它们。然后使用同一个库中的序列化方法创建一个新的元数据文件,最后将这些文件重新组合在一起。
像这样:
<?php
list ($before, $metadata, $after) = preg_split("/\n----*\n/ms", file_get_contents($argv[1]));
$yaml = yaml_parse($metadata);
$yaml_copy = [];
foreach ($yaml as $k => $v) {
// copy the data you wish to preserve to $yaml_copy
if (...) {
$yaml_copy[$k] = $yaml[$k];
}
}
file_put_contents('new/'.$argv[1], $before."\n---\n".yaml_emit($yaml_copy)."\n---\n".$after);
(这只是一个未经测试的草稿,没有错误检查。)
你可以像这样用 gawk 来做:
gawk 'BEGIN {RS="---"; FS="[=10=]0" } (FNR == 2) { print "---"; split(, fm, "\n"); for (line in fm) { if ( fm[line] ~ /^(ID|post_title|author|post_date|layout|published):/) {print fm[line]} } print "---" } (FNR > 2) {print}' post1.html > post1_without_frontmatter_fields.html
您基本上是想编辑文件。这就是 sed(流编辑器)的用途。
sed -e s/^ID:(*)$^post_title:()$^作者:( )$^postdate:()$^layout:()$^published:()$ /ID:\npost_title:\nauthor:\npostdate:\nlayout:\npublished:\6/g
YAML(以及其他相对自由的格式,如HTML、JSON、XML)最好不要使用正则表达式进行转换,很容易为一个例子工作并打破下一个有额外的空格、不同的缩进等。
在这种情况下使用 YAML 解析器并非易事,因为许多人要么期望文件中有一个 YAML 文档(并且 barf 在 Markdown 部分是无关的东西),要么期望文件中有多个 YAML 文档(并且 barf 因为Markdown 不是 YAML)。此外,大多数 YAML 解析器会丢弃有用的东西,例如注释和重新排序映射键。
我多年来一直为我的 ToDo 项目使用类似的格式(YAML header,然后是 reStructuredText),并使用一个小的 Python 程序来提取和更新这些文件。给出这样的输入:
---
ID: 51 # one of the key/values to preserve
post_title: Here's my post title
author: Frank Meeuwsen
post_date: 2014-07-03 22:10:11
post_excerpt: ""
layout: post
permalink: >
https://myurl.com/slug
published: true
sw_timestamp:
- "399956"
sw_open_thumbnail_url:
- >
https://myurl.com/wp-content/uploads/2014/08/Featured_image.jpg
sw_cache_timestamp:
- "408644"
swp_open_thumbnail_url:
- >
https://myurl.com/wp-content/uploads/2014/08/Featured_image.jpg
swp_open_graph_image_data:
- '["https://i0.wp.com/myurl.com/wp-content/uploads/2014/08/Featured_image.jpg?fit=800%2C400&ssl=1",800,400,false]'
swp_cache_timestamp:
- "410228"
---
additional stuff that is not YAML
and more
and more
这个程序 ¹:
import sys
import ruamel.yaml
from pathlib import Path
def extract(file_name, position=0):
doc_nr = 0
if not isinstance(file_name, Path):
file_name = Path(file_name)
yaml_str = ""
with file_name.open() as fp:
for line_nr, line in enumerate(fp):
if line.startswith('---'):
if line_nr == 0: # don't count --- on first line as next document
continue
else:
doc_nr += 1
if position == doc_nr:
yaml_str += line
return ruamel.yaml.round_trip_load(yaml_str, preserve_quotes=True)
def reinsert(ofp, file_name, data, position=0):
doc_nr = 0
inserted = False
if not isinstance(file_name, Path):
file_name = Path(file_name)
with file_name.open() as fp:
for line_nr, line in enumerate(fp):
if line.startswith('---'):
if line_nr == 0:
ofp.write(line)
continue
else:
doc_nr += 1
if position == doc_nr:
if inserted:
continue
ruamel.yaml.round_trip_dump(data, ofp)
inserted = True
continue
ofp.write(line)
data = extract('input.yaml')
for k in list(data.keys()):
if k not in ['ID', 'post_title', 'author', 'post_date', 'layout', 'published']:
del data[k]
reinsert(sys.stdout, 'input.yaml', data)
你得到这个输出:
---
ID: 51 # one of the key/values to preserve
post_title: Here's my post title
author: Frank Meeuwsen
post_date: 2014-07-03 22:10:11
layout: post
published: true
---
additional stuff that is not YAML
and more
and more
请注意 ID
行的注释已妥善保留。
¹ 这是使用 ruamel.yaml YAML 1.2 解析器完成的,它试图在 round-trips 上保留尽可能多的信息,我是作者。
您也可以使用 python-frontmatter
:
import frontmatter
import io
from os.path import basename, splitext
import glob
# Where are the files to modify
path = "*.markdown"
# Loop through all files
for fname in glob.glob(path):
with io.open(fname, 'r') as f:
# Parse file's front matter
post = frontmatter.load(f)
for k in post.metadata:
if k not in ['ID', 'post_title', 'author', 'post_date', 'layout', 'published']:
del post[k]
# Save the modified file
newfile = io.open(fname, 'w', encoding='utf8')
frontmatter.dump(post, newfile)
newfile.close()
如果您想查看更多示例,请访问 this page
希望对您有所帮助。