英文:
Script to generate Markdown files with embedded PlantUML diagrams for GitLab's PlantUML renderer
问题
I understand your request. Here's the translated content:
"我正在设置一个存储由多个Markdown文档组成的软件文档的存储库,并希望能够嵌入PlantUML图表。该存储库托管在Gitlab中,其中包括PlantUML渲染器,但不允许预处理,并因此不允许使用!include
子句引用其他文件中的图表。
我想要一个Bash或Python脚本,它可以:
- 搜索所有的.md文件,并将它们的内容追加到一个新文件"all-docs.md"中。
- 在文件"all-docs.md"中搜索所有的
!include [FILEPATH]
子句,并将位于该文件[FILEPATH]中的@startuml
和@enduml
之间的内容替换为"all-docs.md"中的内容。
例如:
"all-docs.md"在某个部分包含:
这是Profile类图:
``plantuml
@startuml
!include ./data-models/profile.puml
Profile o-- UidObject
@enduml
而profile.puml的内容是:
@startuml
class Profile <UidObject> {
+ string name
+ string email
+ string phone
+ Date birthDate
}
@enduml
脚本运行后的结果将是在"all-docs.md"中拥有以下内容:
这是Profile类图:
``plantuml
@startuml
class Profile <UidObject> {
+ string name
+ string email
+ string phone
+ Date birthDate
}
Profile o-- UidObject
@enduml
该存储库具有以下结构。
/
├── assets/
├── docs/
├── uml/
- "assets/"目录包含各种资源,如图像、图标和其他资源。
- "docs/"目录包含文档(Markdown文件)。
- "uml/"目录包含包含用于生成软件文档的图表的PlantUML源文件。"
英文:
I am setting up a repository to store software documentation consisting of several documents which are written in Markdown, and I want to be able to embed PlantUML diagrams in them. The repository is hosted in Gitlab, which includes a PlantUML renderer but does not allow preprocessing and therefore using the !include
clause to reference diagrams in other files.
I would like to have a bash or python script that:
- Searches all .md files and append their content one after the other in a new file "all-docs.md".
- Searches in that file "all-docs.md" for all the
!include [FILEPATH]
clauses and replace the content which is between@startuml
and@enduml
from that file [FILEPATH] into "all-docs.md".
For example:
"all-docs.md" contains in certain part:
Here is the Profile class diagram:
``plantuml
@startuml
!include ./data-models/profile.puml
Profile o-- UidObject
@enduml
``
And profile.puml content is:
@startuml
class Profile <UidObject> {
+ string name
+ string email
+ string phone
+ Date birthDate
}
@enduml
The result after the script will be to have in "all-docs.md":
Here is the Profile class diagram:
``plantuml
@startuml
class Profile <UidObject> {
+ string name
+ string email
+ string phone
+ Date birthDate
}
Profile o-- UidObject
@enduml
``
The repo has the following structure.
/
├── assets/
├── docs/
├── uml/
- The
assets/
directory contains various assets such as images, icons, and other resources. - The
docs/
directory contains the documents (markdown files) - The
uml/
directory contains contains PlantUML source files that are used to generate diagrams for the software documentation.
答案1
得分: 1
A bash
和 find
解决方案,使用您提供的输入/文件,类似于:
#!/usr/bin/env bash
#: 在所有-docs.md文件中查找并连接docs/目录中的所有.md文件。
find docs/ -type f -name '*.md' -exec sh -c 'cat -- "$@" >> all-docs.md' sh {} +
#: 解析all-docs.md文件并
#: 创建/打印所需的结果/输出。
while IFS= read -r line; do
if [[ $line == "!include"* ]]; then
temp=${line#!include *}
mapfile -t plum < "$temp" &&
unset -v 'plum[-1]' &&
printf '%s\n' "${plum[@]:1}"
else
printf '%s\n' "$line"
fi
done {fd}< all-docs.md
- 将代码的最后一行替换为
done {fd}< all-docs.md > tempfile && mv tempfile all-docs.md
- 如果您对输出满意并需要对
all-docs.md
进行永久更改。
或者直接解析find
的输出,而不创建all-docs.md
,类似于:
#!/usr/bin/env bash
##: 在docs/目录中查找以.md结尾的所有文件
##: 连接所需文件的所有内容。
find docs/ -type f -name '*.md' -exec sh -c 'cat -- "$@"' sh {} + | {
while IFS= read -r line; do
[[ $line != "!include"* ]] && { ##: 如果行中没有模式!include。
printf '%s\n' "$line" ##: 打印行。
}
[[ $line == "!include"* ]] && { ##: 如果行包含模式!include,解析它。
temp=${line#!include *} ##: 将FILE_PATH提取到名为temp的变量中。
if [[ -s "$temp" ]]; then ##: 如果变量是现有的非空文件。
mapfile -u3 -t plum 3< "$temp" && ##: 提取所需结果。
unset -v 'plum[-1]' &&
printf '%s\n' "${plum[@]:1}"
else
printf '%s\n' "$line" ##: 否则只打印行。
fi
}
done
}
- 如果创建
all-docs.md
是必须的要求,然后更改最后一行为:
} > all-docs.md
mapfile
(又名readarray
)和{fd}
都需要bash v4+
。
英文:
A bash
and find
solution with your given input/files, something like:
#!/usr/bin/env bash
#: Find and concatenate all .md files from docs/ directory in all-docs.md file.
find docs/ -type f -name '*.md' -exec sh -c 'cat -- "$@" >> all-docs.md' sh {} +
#: Parse the all-docs.md file and
#: create/print the desired result/output.
while IFS= read -ru "$fd" line; do
if [[ $line == "!include"* ]]; then
temp=${line#!include *}
mapfile -t plum < "$temp" &&
unset -v 'plum[-1]' &&
printf '%s\n' "${plum[@]:1}"
else
printf '%s\n' "$line"
fi
done {fd}< all-docs.md
- Replace the last line of the code to
done {fd}< all-docs.md > tempfile && mv tempfile all-docs.md
- If you're satisfied with the output and permanent changes needs to be made for
all-docs.md
.
Or just parse the output of find
directly without creating the all-docs.md
, something like:
#!/usr/bin/env bash
##: Find all the files ending in .md in the docs/ directory
##: conCATenate all the contents of the files in question.
find docs/ -type f -name '*.md' -exec sh -c 'cat -- "$@"' sh {} + | {
while IFS= read -r line; do
[[ $line != "!include"* ]] && { ##: If line does not have the pattern !include.
printf '%s\n' "$line" ##: Print the line as is.
}
[[ $line == "!include"* ]] && { ##: If line has the pattern !include parse it.
temp=${line#!include *} ##: Extract FILE_PATH in a variable named temp.
if [[ -s "$temp" ]]; then ##: If variable is an existing non-empty file.
mapfile -u3 -t plum 3< "$temp" && ##: Extract the desired result.
unset -v 'plum[-1]' &&
printf '%s\n' "${plum[@]:1}"
else
printf '%s\n' "$line" ##: Otherwise just print the line as is.
fi
}
done
}
If creating the all-docs.md
is a must/requirement, then change the last line to:
} > all-docs.md
- Both
mapfile
akareadarray
and{fd}
requiresbash v4+
答案2
得分: 0
I was able to find a solution myself using Python.
- 使用Python找到了自己的解决方案。
- 定义一个名为
replace_include_with_content
的新函数。该函数以一行作为输入,检查它是否匹配!include子句的模式。如果匹配,它会提取包含路径,构建到相应UML文件的相对路径,读取其内容并返回。否则,它将原样返回该行。 - 遍历排序后的Markdown文件,读取它们的内容。在此循环内,遍历每个文件内容的行。对于每一行,我们调用
replace_include_with_content
函数来检查是否需要替换它。
英文:
I was able to find a solution myself using Python.
- Get a list of markdown files in the source "docs" directory and sort them based on their index prefix (files are named "0.introduction.md", "1.general.md", etc):
- Define a new function called
replace_include_with_content
. This function takes a line as input and checks if it matches the pattern for the !include clause. If it does, it extracts the include path, constructs the relative path to the corresponding uml file, reads its content, and returns it. Otherwise, it returns the line as is. - Iterate through the sorted markdown files, read their contents. Within this loop, iterate over the lines of each file's content. For each line, we call the
replace_include_with_content
function to check if it needs to be replaced.
import os
import re
docs_dir = "docs"
uml_dir = "uml"
output_file = "all-docs.md"
# Get a sorted list of markdown files in the source directory
markdown_files = sorted(
,
key=lambda x: int(x.split(".")[0])
)
# Replace function
def replace_include_with_content(line):
# Check if the line matches the pattern for !include clause
match = re.match(r"!include\s+(.+)", line)
if match:
include_path = match.group(1)
uml_file_path = os.path.join(uml_dir, include_path)
# Read the content of the referenced UML file
with open(uml_file_path, "r") as uml_file:
uml_content = uml_file.readlines()
filtered_content = []
include_next_line = False
for uml_line in uml_content:
if uml_line.strip() == "@startuml":
include_next_line = True
elif uml_line.strip() == "@enduml":
include_next_line = False
elif include_next_line:
filtered_content.append(uml_line)
return "".join(filtered_content)
return line
# Open the output file in write mode
with open(output_file, "w") as outfile:
for file in markdown_files:
# Open each markdown file in read mode
with open(os.path.join(docs_dir, file), "r") as infile:
content = infile.readlines()
for line in content:
# Replace the !include clause with the content from the referenced UML file
replaced_line = replace_include_with_content(line)
outfile.write(replaced_line)
outfile.write("\n")
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论