bashup / mdsh
基于 Markdown 的多语言脚本编程
This package is auto-updated.
Last update: 2024-09-29 05:01:54 UTC
README
mdsh
是一个 Markdown 文件编译器和解释器,用于 Bash 脚本。它可以用于 #!
行来使 Markdown 文件可执行,或者可以作为独立工具从 Markdown 文件生成无依赖、可分发的 Bash 脚本。
默认情况下,mdsh
只考虑 shell
代码块为 Bash 代码,但您也可以使用 @mdsh
块来定义其他语言的处理器。例如,此脚本将通过管道将 python
标记的代码块传递给 python
命令来运行它们
#!/usr/bin/env mdsh # Hello World in Python The following code block is executed at compile time (due to the `@mdsh`). (The first word on the opening line could be `shell` or `sh` or anything else, as long as the second word is `@mdsh`.) ```bash @mdsh mdsh-lang-python() { python; } ``` Now that we've defined a language handler for `python`, this next code block is translated to shell code that runs python with the block's contents on stdin: ```python print("hello world!") ```
运行上述 Markdown 文件产生与以下等效 Bash 脚本相同的结果
#!/usr/bin/env bash { python; } <<'```' print("hello world!") ```
mdsh
支持处理任何您可以为它编写 Bash 代码片段的语言块,甚至允许您编写 "编译时" 代码来将包含元数据或 DSL 片段的块转换为 Bash 代码。结果可以是实时执行以进行开发,也可以通过 mdsh --compile
部署/分发。编译后的脚本不包含任何 @mdsh
代码,也没有任何隐藏的运行时依赖项:mdsh --compile
输出的所有内容都是您提供给它的代码或数据,或者由您提供的 Bash 代码生成的!
内容
安装
mdsh
可以以下方式之一安装
- 使用 basher,通过
basher install bashup/mdsh
- 使用 composer,通过
composer require bashup/mdsh:dev-master
(将其添加到您的项目)或composer global require bashup/mdsh:dev-master
(全局安装) - 使用 git,通过克隆此存储库并将
bin/mdsh
文件复制或链接到您的PATH
目录上,或者 - 直接 下载脚本 到您的
PATH
目录,然后对它运行chmod +x
)
用法
运行 mdsh
markdownfile args... 将从 markdownfile 中读取并翻译无缩进、三重反引号围栏的代码块到 Bash 代码,基于块上列出的语言和任何您定义的翻译规则。然后运行生成的翻译脚本,将 args 作为脚本的参数传递。
标记为 shell
的块被视为 Bash 代码,并直接复制到翻译脚本中。因此,传递给 mdsh
的参数(在 Markdown 文件路径之后)可用作 $1
、$2
等,在 shell
块的顶层代码中,就像在常规 Bash 脚本中一样。
(通常,您不会直接运行mdsh
,而是将#!/usr/bin/env mdsh
放在Markdown文件的第一行,然后用chmod +x
使其可执行。这样,您的脚本用户就不需要做任何特殊操作即可运行它。)
您还可以使用mdsh --compile
文件1 文件2...将一个或多个Markdown文件转换为bash代码,并将结果输出到标准输出。(-
文件名表示“从标准输入读取”。)这可以用于调试,或者制作一个不需要用户拥有mdsh
即可运行的脚本的可分发版本。
(还有一个mdsh --eval
文件名选项,与--compile
类似,但只接受一个非stdin文件,并在文件末尾输出特殊代码以支持可源文件。请参阅下文关于制作可源脚本的部分以获取更多详细信息。)
--eval
和--compile
都可以用--out
文件名开头,在这种情况下,如果编译或运行成功且没有错误,则替换文件名的内容。(mdsh
的输出将在内存中缓冲,然后在成功完成后一次性输出。如果文件已存在,其权限将保持不变。)
数据块
默认情况下,不缩进的三重反引号块的内容被当作数据处理:其内容将被添加到按块上的语言命名的bash数组中,例如
# Data Arrays Example Blocks without a defined language processor get translated to a variable assignment like `mdsh_raw_json+=(\#\ block\ 0)` at that point in the generated script: ```json { "hello": "world" } ``` ```shell echo "${mdsh_raw_json[0]}" # prints '{ "hello": "world" }' ``` ```json { "this is": "great" } ``` ```shell echo "${mdsh_raw_json[0]}" # prints '{ "hello": "world" }' echo "${mdsh_raw_json[1]}" # prints '{ "this is": "great" }' ``` ## Naming Rules Language names are *case sensitive*, and non-identifier characters in language names become `_` in variable names: ```C++ // hey ``` ```shell echo "${mdsh_raw_C__[0]}" # prints '// hey' ```
当然,如果您能自动化这些块的处理,那就更好了,这样您就不需要每个块后面都跟着另一个shell
块来处理它了!这就是为什么下一节是关于...
处理非 shell
语言
为了自动化处理非shell
语言块,您可以定义一个或多个@mdsh
块,包含“钩子函数”。@mdsh
块有点像Makefile,因为它们定义了基于所使用的语言的如何构建脚本的部分的规则。
这些构建规则通过定义特别命名的bash函数来指定。与shell
块中的函数不同,这些函数不是您脚本的一部分,因此不能直接调用。相反,mdsh
本身会在后续块的语言与函数名称之一匹配时调用它们(或复制它们的源代码)。
Markdown代码块的语言通常是开头的反引号后的一个单词。但如果出现多个单词,mdsh将认为语言是第二个单词(如果它以@
开头),或者整行扁平化为单个变量名。一些示例翻译
函数名称的解析如下
mdsh-lang-X
函数是当遇到语言X
的代码块时要运行的代码模板。它的函数体被复制到翻译后的脚本中,作为一个bash复合语句(即在花括号{...}
中),它将执行块内容作为标准输入。其标准输出与整体脚本的相同。mdsh-compile-X
函数在编译时调用,带有块内容作为$1
,并必须将其输出到其标准输出的bash源代码转换。块的完整原始语言标签在$2
中,代码块的起始行号在$3
中。- 如果既不存在
mdsh-lang-X
函数也不存在mdsh-compile-X
函数,则在编译时将调用mdsh-misc
,使用原始语言标记作为$1
,并将块内容作为$2
。mdsh-misc
的输出将被添加到编译后的脚本中。(mdsh-misc
的默认实现将输出代码,用于保存块内容,如上所述的数据块部分所述。) mdsh-after-X
函数是在遇到语言块X
后要运行的代码的模板。其函数体被复制到翻译后的脚本中,作为mdsh-lang-X
体、mdsh-compile-X
输出或mdsh_raw_X+=(contents)
语句之后的块。它不接收块源代码,所以其标准输入和输出是脚本的本身。
如果同时存在 mdsh-lang-X
和 mdsh-compile-X
函数,则 mdsh-lang-X
优先。定义其中的任何一个也禁用了 $mdsh_raw_X
功能:只有不可翻译的“数据”块被添加到数组中。
然而,如果没有 mdsh-lang-X
或 mdsh-compile-X
,则 mdsh-after-X
函数可以读取最新块的内容,从 ${mdsh_raw_VARNAME[-1]}
中读取(除非您已替换默认的 mdsh-misc
实现)。如果您不取消设置数组,它将随着遇到更多该语言的块而不断增长。
注意:这些函数名是 区分大小写的,所以带有大写字母 C
的块不会触发与带有小写字母 c
的块相同的函数,反之亦然。此外,请注意,由于 mdsh
块是在编译时执行的,它们没有访问脚本的参数或 I/O:在这些块中,您只能定义钩子函数。
最后,请记住,您通常不应该在 @mdsh
块中放入任何代码,除非您有意进行元编程或代码生成。这是因为 @mdsh
块不是翻译脚本的一部分,它们是翻译过程的一部分。所以您在其中定义的任何函数在脚本实际运行时将不可用,并且您对变量所做的任何更改在脚本实际执行时也不会存在。
高级块编译技术
一旦您习惯了做一些 mdsh-lang-X
函数,为什么不尝试一些 mdsh-compile
呢?
例如,在jqmd
项目中,我最初有一些这样的代码
YAML() { JSON "$(echo "$1" | yaml2json -)"; } mdsh-lang-yaml() { YAML "$(cat)"; }
这效果相当不错,但是,由于 YAML 是一个常量值,为什么不在编译期间将其转换为 JSON 呢?这样,我们可以消除运行时的开销(如果我们保存并重新运行编译后的脚本)
mdsh-compile-yaml() { printf 'JSON %q\n' "$(echo "$1" | yaml2json)"; }
注意这两个函数之间的区别:lang
函数是一个代码 模板。mdsh
将其主体复制到您的脚本源中,生成如下所示的代码
{ YAML "$(cat)" } <<'```' ... yaml data here ... ```
compile
函数只是立即运行 yaml2json
,然后输出翻译后的数据,如下所示
JSON ...shell-quoted json here...
顺便说一句,注意使用 printf
与 %q
的用法--这会导致数据被正确转义,以便作为命令行参数使用。(当您直接生成代码时,请务必正确转义这些值。当您需要将变量数据插入到生成的代码中时,始终使用带有常量字符串格式的 printf
,其中包含 %q
占位符,用于任何独立的参数。)
顺便提一下,compile
函数可以访问实际的块文本,这意味着您可以执行任何类型的代码生成。例如,我可以从 yaml2json
的输出中取出,然后运行 jq
,然后遍历输出并编写根据结果设置变量的 bash 代码,或者根据规范生成子命令的代码,或者甚至可能从其中生成一个参数解析器。这些代码生成技术有各种各样的有趣可能性!
编译时变量
除了它们的定位参数之外,编译时钩子如 mdsh-misc
和 mdsh-compile-X
还会接收到一些变量,这些变量在解析特殊块标题或生成错误消息时可能会有帮助。
${tag_words[@]}
是从原始块开头的行中分离的空白分隔的单词数组。例如,如果一个块以```foo @bar.baz spam
打开,那么tag_words=([0]="foo" [1]="@bar.baz" [2]="spam")
。(${#tag_words[@]}
是单词的数量。)$mdsh_lang
是 mdsh 看到的块的语言,即mdsh-lang-X
中的X
。(因此,它是${tag_words[0]}
、${tag_words[1]#@}
或用_
替换非标识符字符的整行内容。)- 如果正在编译的是文件,则
$MDSH_SOURCE
是源文件名。 $block_start
是块在原始源中的起始行号。$mdsh_block
包含块的文本。$mdsh_tag
包含原始块开头的行(即 tag_words 的未拆分形式)。
(这些变量也可以由编译时命令块使用,如下一节所述。)
程序性块生成
mdsh-block
函数允许您以编程方式生成指定语言的代码块。这对于条件块等非常有用。例如,这个 if-env
函数可以在命令块中使用来生成在运行时检查 $WP_ENV
的值的代码,并根据条件执行块的正文。
```shell @mdsh if-env() { printf -v REPLY '|%q' "$@" echo "case \$WP_ENV in ${REPLY#|})" mdsh-block "$mdsh_lang" "$mdsh_block" "$block_start" echo echo "esac" } ``` ```css !if-env dev staging /* This CSS is only used in dev and staging */ ```
mdsh-block
函数接受最多四个参数:一个语言、一个块主体、一个起始行号和一个“原始”语言标签(如果未指定,则默认为语言)。前三个参数是可选的,如果省略,则默认为 $mdsh_lang
、$mdsh_block
和 $block_start
。(这意味着上面的代码可以不传递任何参数直接调用 mdsh-block
!)
mdsh-block
遵循标准的语言查找逻辑,首先查找 mdsh-lang-X
,然后是 mdsh-compile-X
,然后回退到 mdsh-misc
,如果适用,还会克隆 mdsh-after-X
。它不支持命令块或语言别名,因此不能使用 @
、+
、!
或 |
表达式。它旨在用于编译时代码,即 !
命令块、@mdsh
块以及 mdsh-misc
和 mdsh-compile-X
函数等处理器。
命令块和参数
有时您只有一个需要以特定方式处理的块,或者特定语言的每个块在编译或执行时都需要唯一的参数。对于这些场景,您可以定义“命令块”。
命令块是语言标签的第二词以 |
、+
或 !
开头的代码块。
- 如果是
|
,则语言标签的其余部分在运行时执行,并将块的正文作为标准输入(就像mdsh-lang-X
函数体一样),并将 shell 变量mdsh_lang
设置为语言标签的第一词。 - 如果是
+
,则语言标签的其余部分在运行时执行,并将块的正文作为额外的命令行参数,并将 shell 变量mdsh_lang
设置为语言标签的第一词。 - 如果是
!
,则语言标签的其余部分在 编译 时执行,并将块的正文放在$1
中,必须将编译后的代码输出到标准输出(就像mdsh-compile-X
函数一样)。完整的语言标签在$2
中,代码块的起始行号在$3
中。所有标准的 编译时变量 都可用,包括mdsh_lang
、tag_words
、block_start
以及可能还有MDSH_SOURCE
。
在所有上述情况下,$mdsh_lang
被设置为语言标签的第一个单词,但不会被包含在执行的命令行中。(它被假定是一个语法高亮提示,但也可以用作参数,如果你的代码引用了 $mdsh_lang
。)
命令块会覆盖正常的语言功能查找,因此不会查找或执行任何 mdsh-after-X
、mdsh-lang-X
或 mdsh-compile-X
函数。因此,以下代码作为 mdsh 的输入
```json !printf "echo %q\n" "# line $3, $mdsh_lang block:" "def example: $1;" {"foo": "bar"} ``` ```html +echo "The $mdsh_lang is:" <html /> ``` ```python |python - "a $mdsh_lang block" import sys; print "hello, world from "+sys.argv[1] ```
将编译成以下 shell 脚本
echo \#line\ 49, json block: echo $'def example: {"foo": "bar"}\n;' echo "The html is:" $'<html />\n' python - "a python block" <<<'```' import sys; print "hello, world from "+sys.argv[1] ```
注意,命令块标签的两种形式都可以包含几乎任意的 bash 代码,包括管道、替换等,但 绝对不能 包含反引号字符,因为 Commonmark 规范 要求将这些行视为带有内联代码的普通文本,而不是带围栏的代码块的开始。因此,mdsh 和其他符合 Commonmark 的工具甚至不会将这一行识别为代码块的开始,输入文件的其余部分解析将被打乱,代码将被解释为文本,反之亦然。
技巧和技术
文献测试
使用 cram 功能测试工具测试的 mdsh 创建的文档可以包含示例 shell 会话。只需将 cram 的缩进级别设置为 4,并使用 4 个空格缩进的块来为 cram 测试的示例,可选地用 ~~~
带围栏代码块包裹,如下所示
$ echo "hello world!" hello world!
Cram 寻找 $
或 >
和一个空格,缩进到一定级别,然后运行命令(s)并验证输出。因此,cram 需要知道你使用的是哪种缩进。
mdsh 忽略 4 个空格缩进和 ~~~
带围栏块,因此它不会被你的示例弄混淆。你可以通过包含类似 ~~~bash
或 ~~~shell
的语言标签来让 github 对你的示例进行语法高亮。
解释如何使用 cram 的所有细节超出了本指南的范围,但在最简单的情况下,使用 cram --indent 4 mydocument.md
将运行 mydocument.md
中的任何 4 个空格缩进的示例。
(注意,cram
实际上并不理解 markdown,因此它将尝试运行任何以 "$ "
或 "> "
开头并在指定缩进处的代码。通常,以这种方式开始的非示例代码可以略微缩进或进一步缩进,以便 cram 忽略它。在 此 PR 合并之前,你将想要使用 此 cram 分支,该分支已被修补以忽略不是示例直接部分的缩进行。如果你使用 .devkit 的 cram 模块,你的测试将自动使用正确版本。)
从生成的脚本中排除块
如果你的脚本包含大量包含带围栏代码块的文档示例,你可能希望排除这些示例的处理或复制到 bash 变量中。你可以通过两种主要方式来实现这一点。
首先,你可以更改指示某些代码块的方式。所有这些目前都被 mdsh
忽略,不会生成任何代码
-
使用四个空格缩进的代码块,而不是带围栏的
-
使用
~~~X
而不是```X
带围栏的代码块 -
使用多于三个反引号或缩进的代码块
-
没有指定语言的代码块
-
命令为空或无操作的立即命令块。(即,语言为单个单词后跟一个空格和一个
!
字符的块,可选地后跟 shell 注释或无操作 shell 命令。)例如,这两个代码块都将从编译后的脚本中省略。```python ! # mdsh won't do anything with this block raise RuntimeError("This won't actually run!") ``` ```shell ! echo "No comment is actually needed. This block is ignored, too." ```
另外,您可以在 mdsh 块中定义空的 mdsh-compile-X
函数,针对您想排除编译的每种语言,或者定义一个什么也不做的 mdsh-misc
函数。(这将完全禁用数据块;有关 mdsh-misc
的更多信息,请参见下面的元编程和代码生成部分。)
使 Markdown 文件可执行(并可编辑)
如果您想直接运行 markdown 文件,您可以使用 chmod +x
命令并给它一个 shebang 行,例如 #!/usr/bin/env mdsh
。这样,您就可以直接运行 somescript.md args..
,而无需在前面键入 mdsh
。
如果您想去掉脚本上的 .md
扩展名,您可能还需要添加一行代码,告诉 Github、您的编辑器等,该文件仍然是 markdown,例如:
#!/usr/bin/env mdsh <!-- ex: set syntax=markdown : --> <!-- -*- mode: markdown -*- -->
这将告诉 Github、atom(带有 vim-modeline
包)、vim、emacs 和其他编辑器/显示工具,该文件实际上是 Markdown。
(或者,您可以在文件上保留 .md
扩展名进行编辑,但使用无扩展名的符号链接来运行它,无需键入 .md
。)
制作可溯源的脚本(并处理 $0)
通常,编写脚本的方式应使它们定义的函数可供其他脚本使用,通常通过 source
它们。这导致在 bash 脚本末尾编写如下代码的常见模式,以便仅在脚本不是由 source 调用时运行脚本的 main 函数。
if [[ $0 == $BASH_SOURCE ]]; then # Not `source`d: run as script my-main "$@" exit $? fi
这种做法在 mdsh
中也没有不同:无论您是用 mdsh
#!
行执行脚本,还是编译并运行它,变量 $0
和 $BASH_SOURCE
只有在您的程序作为脚本运行时才会相等。
遗憾的是,当 mdsh
通过 #!
行运行时,确保这些值相等的方法是它们都为 空。这意味着 $0
将是一个空字符串,这可能会干扰像在“用法”消息中使用它来表示程序名称之类的常见做法。
作为解决方案,mdsh
定义了一个 $MDSH_ZERO
参数,如果您的脚本被直接调用,则定义该参数。它包含如果您的脚本被编译,则 $0
和 $BASH_SOURCE
将具有的值。您可以使用 ${0:-$MDSH_ZERO}
便携地检索调用脚本名称,无论您的脚本是否编译或解释。
但这仍然不能使您的脚本可 source。毕竟,它是 markdown,而不是 bash。因此,如果另一个脚本尝试 source
它,它将遇到各种语法和其他错误。
为了解决这个问题,我们需要一个 shelldown 标头:两行在 bash 中执行的代码,但在大多数 markdown 渲染中都是隐藏的,并且仍然告诉我们的编辑器(和 Github)忽略 #!
行,并将文件视为 markdown,而不是 bash
#!/usr/bin/env bash : ' <!-- ex: set ft=markdown : '; eval "$(mdsh --eval "$BASH_SOURCE")" # --> # My Awesome Script ...code goes here...
现在,我们的脚本使用 bash
语法,足以编译文档并运行结果。使用 mdsh --eval
编译脚本将创建一个安全版本的脚本,可以 eval
和 source
,通过在代码末尾添加一个执行 return $?
或 exit $?
的额外行,具体取决于脚本是否被 source
。(这确保了 bash 在处理文件时停止处理,在它能够被随后的 markdown 内容弄混淆之前。)
因此,该文件可以无问题地运行或 source
。更重要的是,您可以使用 mdsh --compile
它创建一个纯 bash 脚本(仍然可运行和 source
),无需进行任何代码更改。
这是因为当您直接运行或 source
脚本时,您实际上执行的是将编译的 相同 代码,在相同的环境中,$BASH_SOURCE
指向实际文件。(并且 $0
与它匹配,除非它正在被 source
。)
为什么不用这种方法呢?当然可以。如果你不介意把它复制粘贴到所有新的脚本中,那就按你的意愿去做吧!然而,对于一次性的脚本,其中没有使用到$0
的值,你也不关心编辑器支持,使用mdsh
的#!
行确实是一个更简单的方法。
语法高亮和语言别名
由于mdsh
不是广为人知的语言,GitHub和其他Markdown编辑/处理工具通常不知道如何正确地突出显示它们。作为一种权宜之计,你可以给一个shell @mdsh
的块标签,大多数工具会将其解释为用于突出显示的shell块。例如:
```shell @mdsh echo 'echo "Most tools will highlight this block as shell script"' ```
元编程和代码生成
从mdsh
标记的块中产生的任何输出都将成为在块出现的位置生成的bash脚本的一部分。这意味着你可以简单地使用cat
其他bash文件来包含它们(或使用mdsh-embed
;见下一节),或者在那里生成任何其他你想要的代码。这可以是一个有用的替代方案,代替使用source
来加载函数,因为它意味着生成的脚本可以被--compile
到一个不包含其他模块的单个文件中。
如果你要以某种方式编程处理单个块(例如,从它们的语言标签中提取文件名),你可以定义一个mdsh-misc
函数。对于每个没有mdsh-lang-X
或mdsh-compile-X
函数的块,mdsh-misc
会使用语言标签和块内容作为参数被调用,并且它的输出会被附加到文件中的那个位置编译的脚本上。
所以,例如,当运行此脚本时,它将文本块的输出内容保存到file1.txt
中。
```mdsh mdsh-misc() { if [[ $1 == *'>'* ]]; then echo -n "$2" >"${1#*>}"; fi } ``` ```text >file1.txt Some text goes here! ```
当然,mdsh-misc
的潜在应用远远不止是将块写入文件。例如,你可以
- 模拟其他文献编程工具的“编织”功能(通过让
mdsh-misc
根据块的标签信息将块内容保存到不同的变量中,然后在程序结束时使用一个输出保存的块的mdsh
块) - 将标签解释为处理块的参数
- 将以
|
开头的块标签视为预处理块的管道
...以及你能够想象到的任何其他事情。
"静态链接" 用于分发
你可以使用mdsh-embed
函数将其他bash模块的源代码嵌入到你的脚本中。在mdsh
块内部调用mdsh-embed
modulename将会在PATH
中搜索modulename(除非modulename包含一个/
),然后输出其源代码,并用heredoc和source
命令包裹起来。(这确保了嵌入的模块知道它被加载了,而不是通过命令行运行,即使嵌入脚本是通过命令行运行的。)
结果是,通过在mdsh
块中使用mdsh-embed
来加载你的模块(而不是在shell
块中使用source
),你可以将你的脚本--compile
到一个“静态链接的可执行文件”。也就是说,你可以创建一个包含所有所需模块的单个文件,这样你的用户就不需要自己安装所有依赖项,也不需要特定的包管理器来安装你的脚本。
扩展 mdsh
或重用其函数
从bash脚本中源mdsh
将定义所有其函数,但不会实际运行一个程序。这允许你更改命令行参数的处理方式,或预先定义额外的语言钩子、拆卸钩子等。(你也可以这样做,以便使用内建的Markdown处理函数。)
(注意,源mdsh
将设置bash到“非官方严格模式”,即-euo pipefail
。mdsh
假设这些设置是有效的,所以更改它们可能会有不良后果。)
如果你编写的是“带有更多语言的mdsh”,你可以这样做
#!/usr/bin/env bash source "$(command -v mdsh)" mdsh-compile-somelang() { # etc. } # ... [[ $0 == $BASH_SOURCE ]] && mdsh-main "$@"
也就是说,只需源码mdsh并定义您的附加语言处理器,然后运行mdsh-main "$@"
:您的脚本将具有与mdsh
相同的命令行界面,但所有帮助信息都将引用您的脚本名称,而不是mdsh
。
添加文件头或尾
如果您的mdsh扩展版本需要向生成的文件添加标题或页脚,您可以定义名为mdsh:file-header
和/或mdsh:file-footer
的函数。--compile
选项将在编译过程的开始和结束时调用这些函数一次,将整个输出封装起来。--eval
的行为类似,但--eval
页脚将出现在mdsh:file-footer
之后。
修改现有函数
在某些情况下,您可能还希望通过替换一些函数来改变mdsh的部分行为。但是,您可以通过使用mdsh-rewrite
来避免将那些函数复制到您的代码中。mdsh-rewrite
是一个通常在mdsh编译器中用于重写mdsh-lang-X
和mdsh-after-X
函数体的函数,但您可以将它改编为类似AOP的bash函数编辑。
例如,jqmd通过在mdsh.--compile
函数的开始和结束处添加函数调用,向使用jqmd --compile
和jqmd --eval
编译的文件添加标题和页脚。
eval "mdsh.--compile() $(mdsh-rewrite mdsh.--compile '{ jqmd-header;' 'jqmd-footer; }')"
(mdsh-rewrite
接受一个函数名称和两个可选字符串,这两个字符串将替换函数体中开头和结尾的花括号行。结果输出到stdout,成为新函数的主体。)
可用函数
以下函数可用于在源码mdsh
的脚本中进行使用或修改
-
mdsh-run
mdfile [cache-key [args...]] -- 以args作为其位置参数($1
、$2
等)的编译后的指定markdown文件source
。如果存在,将使用$MDSH_CACHE
中的目录来缓存编译版本,文件名使用cache-key生成。如果cache-key缺失或为空,则使用mdfile作为缓存键。要设置特定的缓存目录或禁用缓存,请使用与
mdsh-run
相同的行设置它,例如,使用MDSH_CACHE= mdsh-run somefile
来运行somefile
而不缓存。源码或运行mdsh
将默认的MDSH_CACHE
设置为$XDG_CACHE_HOME/mdsh
或$HOME/.cache/mdsh
,具体取决于您的操作系统。 -
mdsh-use-cache
[cachedir] -- 将MDSH_CACHE
设置为cachedir。如果没有提供任何参数,则将MDSH_CACHE
重置为默认值$XDG_CACHE_HOME/mdsh
或$HOME/.cache/mdsh
,具体取决于您的操作系统。 -
run-markdown
mdfile args... -- 以args作为其位置参数($1
、$2
等)source
编译后的mdfile,不使用缓存。 -
mdsh-error
format args... -- 将format args打印到stderr并以错误级别64(EX_USAGE)终止进程。(自动添加换行符到格式字符串。) -
mdsh-compile
-- 接受stdin中的markdown,并在stdout输出bash代码。编译在子shell中执行,因此正在编译的代码中定义的钩子函数不会影响调用者的环境。但是,已经定义在调用者环境中的钩子函数将被用于翻译相关语言块。 -
mdsh-source
-- 与mdsh-compile
类似,但不运行在子shell中,因此任何钩子函数或编译时变量更改将影响调用者,就像包含的源码是包含文档的一部分。 -
mdsh-embed
modulename -- 在PATH
上查找modulename(除非它包含一个/
),并将它的内容包装在source
命令和heredoc中。如果找不到modulename或无法读取它,则返回失败。(注意:与Bash的source
命令不同,此函数不会回退到在当前目录中查找模块。如果您想在当前目录中查找文件,请使用./modulename
。) -
mdsh-rewrite
函数 之前 之后 -- 将函数的正文块输出到标准输出,可选地使用之前和之后替换开闭花括号。 (如果您使用此命令“编辑”函数,请记住替换必须包括开闭花括号,并且闭花括号之前必须是一个换行符、分号或空格。) -
mdsh-make
源文件 目标文件 [命令 参数...] -- 如果目标文件不存在或其时间戳与源文件不同,则将源文件编译为目标文件,在开始编译之前运行命令 参数...。编译(和命令 参数...)在子shell中运行。编译成功后,会更新目标文件,使其时间戳与源文件相同。 -
mdsh-cache
缓存目录 源文件 [键 [命令 参数...]] -- 与mdsh-make
类似,但目标文件作为缓存目录内的文件名自动生成,并在$REPLY
中返回。如果指定了非空键,则使用键而不是源文件来生成目标文件名。如果缓存目录不存在,则会自动创建。生成的文件名是键或源文件的转义版本,这样它们始终位于缓存目录下,即使源文件名包含斜杠也是如此。 -
exit
[代码 [消息 [参数...]]] -- 在显示消息到标准错误后,以代码状态退出(如果没有指定,则默认为$?
),如果提供了参数,则使用消息作为printf
格式字符串进行格式化(自动添加换行符)。