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-runmdfile [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-markdownmdfile args... -- 以args作为其位置参数($1、$2等)source编译后的mdfile,不使用缓存。 -
mdsh-errorformat args... -- 将format args打印到stderr并以错误级别64(EX_USAGE)终止进程。(自动添加换行符到格式字符串。) -
mdsh-compile-- 接受stdin中的markdown,并在stdout输出bash代码。编译在子shell中执行,因此正在编译的代码中定义的钩子函数不会影响调用者的环境。但是,已经定义在调用者环境中的钩子函数将被用于翻译相关语言块。 -
mdsh-source-- 与mdsh-compile类似,但不运行在子shell中,因此任何钩子函数或编译时变量更改将影响调用者,就像包含的源码是包含文档的一部分。 -
mdsh-embedmodulename -- 在PATH上查找modulename(除非它包含一个/),并将它的内容包装在source命令和heredoc中。如果找不到modulename或无法读取它,则返回失败。(注意:与Bash的source命令不同,此函数不会回退到在当前目录中查找模块。如果您想在当前目录中查找文件,请使用./modulename。) -
mdsh-rewrite函数 之前 之后 -- 将函数的正文块输出到标准输出,可选地使用之前和之后替换开闭花括号。 (如果您使用此命令“编辑”函数,请记住替换必须包括开闭花括号,并且闭花括号之前必须是一个换行符、分号或空格。) -
mdsh-make源文件 目标文件 [命令 参数...] -- 如果目标文件不存在或其时间戳与源文件不同,则将源文件编译为目标文件,在开始编译之前运行命令 参数...。编译(和命令 参数...)在子shell中运行。编译成功后,会更新目标文件,使其时间戳与源文件相同。 -
mdsh-cache缓存目录 源文件 [键 [命令 参数...]] -- 与mdsh-make类似,但目标文件作为缓存目录内的文件名自动生成,并在$REPLY中返回。如果指定了非空键,则使用键而不是源文件来生成目标文件名。如果缓存目录不存在,则会自动创建。生成的文件名是键或源文件的转义版本,这样它们始终位于缓存目录下,即使源文件名包含斜杠也是如此。 -
exit[代码 [消息 [参数...]]] -- 在显示消息到标准错误后,以代码状态退出(如果没有指定,则默认为$?),如果提供了参数,则使用消息作为printf格式字符串进行格式化(自动添加换行符)。