m4 宏包

作者:Robert Adams

当您安装 Linux 时,您也安装了许多很棒的程序,其中许多程序您每天都在使用。与此同时,数百个小实用程序也被安装了,仅仅是因为它们是更大、更复杂的应用程序所必需的。然而,许多这些小实用程序本身就非常有用。本文描述了这些二等实用程序之一:m4。

m4 是一个宏处理器,意味着它将其输入复制到输出,并在途中扩展宏。在这方面,它类似于您可能已经熟悉的另一个宏处理器:cpp(C 预处理器)。与 cpp 一样,m4 最初是作为编程语言(Rational FORTRAN)的预处理器编写的;然而,m4 比 cpp 功能更强大、特性更丰富,这使得它不仅仅用于在程序中定义常量,用途更广泛。

作为一个宏处理器,m4 当然提供了定义简单宏的能力,但它的功能远不止于此。您还可以定义参数化宏(带参数的宏),有条件地将文本包含到输出流中,通过递归进行循环,运行 shell 命令并将其输出插入到输出流中,包含文件,执行简单的字符串操作(长度、子字符串搜索、正则表达式搜索等),执行简单的整数算术等等。

以下示例说明了 m4 的许多功能,但本文无法充分展示 m4 可以为您做的一切。请查看 info 页面(请参阅“资源”)以获得完整描述。此外,以下示例是我实际使用的 m4 宏的缩写版本。您可以在我的网页上找到完整的源代码,该网页列在“资源”中。

来自 m4 info 页面的警告

有些人[发现] m4 非常容易上瘾……学习如何编写复杂的 m4 宏集……一旦真正上瘾,用户甚至会为了解决简单问题而追求编写复杂的 m4 应用程序……请注意,m4 可能对强迫症程序员的健康有害。

定义和使用宏

m4 的基本工具是宏。宏使用 define 命令定义。例如,define(`hello', `Hello, World') 定义了一个名为 hello 的宏。请注意引号字符 ` 和 '。它们将单词组合在一起形成短语,当它们包围单个单词时,它们会抑制宏扩展。通常,m4 会扩展宏名称和宏主体中的宏,除非您通过引用告诉它不要这样做。

与 cpp 一样,您不需要任何特殊命令或前缀字符来使用宏。只要宏名称出现在输入流中,m4 就会替换宏主体。

要运行 m4,只需提供要用作输入的文件名。它会读取输入,并在读取过程中扩展宏,并在标准输出上生成文本。

假设我们有以下名为 sample.m4 的文件

define(`hello', `Hello, World')
hello, welcome to m4!

如果我们使用命令运行此文件通过 m4

m4 sample.m4 > sample.txt
它会产生以下输出
Hello, World, welcome to m4!
HTML 示例

正如我上面所说,m4 超越了简单的宏。此示例通过定义一些宏来演示 m4 的一些高级功能,以确保 HTML 页面都具有相同的外观和风格。HTML 纯粹主义者可能可以使用 JavaScript 和 CSS 来做到这一点,但这对于 m4 来说非常容易,并且不需要浏览器做任何特殊的事情。

定义了四个宏:一个用于生成 HTML 标头,第二个用于在 HTML 页面顶部创建横幅,第三个用于在 HTML 页面中创建横幅(如节标题),最后一个用于在页面末尾进行一些整理。我们将这些宏放入一个名为 html.m4 的文件中,并在以后将其用作宏包。

首先,启动 HTML 页面的宏

define(`START_HTML',
`<html>
<head>
  <meta http-equiv="Content-Type" content="text/html;
   charset=iso-8859-1">
  <meta name="Author" content="D. Robert Adams">
  <title>$1</title>
</head>
<body text="#000000"
  ifdef(`BACKGROUND_IMAGE',
        `background="BACKGROUND_IMAGE"')
  bgcolor="#e5e5e5" link="#3333ff"
vlink="#000099"
  alink="#ffffff">
')

第一行定义了一个名为 START_HTML 的宏,它扩展为后面的所有文本。请注意第 7 行上 $1 的使用。它扩展为第一个宏参数,该参数将成为页面的标题(有关如何使用这些宏,请参阅下一节)。另请注意第 11 行上 m4 的 ifdef 宏的使用。ifdef 检查是否已定义宏。如果已定义,则包含第二个参数中给出的文本。在本例中,ifdef 检查是否已定义 BACKGROUND_IMAGE。如果已定义,我们包含 HTML 代码以使用该图像作为网页的背景。

接下来是页面标题宏

define(`PAGE_HEADER',
`<table border="0" background="steel.jpg"
width="100%">
  <tr>
    <td align="left">$1</td>
    <td align="right">$2</td>
  </tr>
</table>
<div align=right>
  Last Modified: esyscmd(`date')
</div>
')

同样,请注意第 4 行和第 5 行上 $1 和 $2 的使用,它们使用宏参数进行扩展。此外,请注意第 9 行上的 esyscmd() 宏。esyscmd() 告诉 m4 运行给定的 shell 命令,并在给定位置插入其输出。在本例中,我们运行“date”以在文档中生成时间戳。

接下来,我们创建用于在页面中创建节横幅的宏。这没有使用您以前没有见过的任何东西

define(`HTML_BANNER',
`<table border="0"
background="steel.jpg"
width="100%">
  <tr>
    <td>
      <img src="$2">
      <h2>$1</h2>
    </td>
  </tr>
</table>
')

必要的最后一部分是关闭 HTML “body”和“html”标记的宏

define(`END_HTML', `</body></html>')
给定上述四个宏,创建网页非常容易。创建一个文件 (index.m4),其中包含对我们的 HTML 宏的调用。唯一的新内容是 include 宏,它插入我们的 HTML 宏
include(`html.m4')
define(`BACKGROUND_IMAGE', `bg.jpg')
START_HTML(`Sample Page')
PAGE_HEADER(`computer.jpg', `Sample HTML
Page')
HTML_BANNER(`img1.jpg', `News')
HTML_BANNER(`img2.jpg', `Downloads')
END_HTML
一旦我们声明了宏和一个使用它们的文件,最后一步是运行 m4 以创建 HTML 页面。命令是
m4 index.m4 > index.html
就是这样!仅用七行代码,我们就创建了一个功能齐全的 HTML 文档。通过使用宏来创建其他 HTML 页面,每个页面都将具有相同的外观和风格。此外,我们可以通过简单地更改宏定义并重新创建 HTML 文件来更改外观。
一个简单的数据库示例

在第二个示例中,我们为课程创建一个考试题库“数据库”。目标是:1) 在单个存储库中管理考试题,2) 能够通过简单地选择要包含的问题来创建 LaTeX 格式的试卷,以及 3) 以同样小的努力生成答案键。

问题数据库由每个问题的 m4 宏组成。我们将宏存储在一个名为 questions.m4 的文件中。例如

define(`QUESTION_1',
  `Why did the chicken cross the road?
  ANSWER(1in, `To get to the other side')
')

显然,此宏将使用问题本身进行扩展,但请注意嵌入式宏 ANSWER 的使用。如果我们正在生成试卷,它会扩展为一英寸的垂直空间,否则,如果我们正在生成答案键,它会扩展为答案。ANSWER 定义为

define(ANSWER, ifdef(`ANSWER_KEY', `Answer: $2',
`\vspace*{$1}'))
如果定义了 ANSWER_KEY,我们包含答案 ($2),否则我们包含一些垂直空间 ($1) 以便学生可以写入答案。

使用问题宏与使用 HTML 宏一样容易。完整的试卷代码存储在一个名为 exam.m4 的文件中

include(examMacros.m4)
EXAM_HEADER(`JOKE 101', `Fall 2001',
`First Exam')
include(questions.m4)
QUESTION_1
QUESTION_2
EXAM_TRAILER

第一行上的“include”包含 EXAM_HEADER 和 EXAM_TRAILER 的代码,它们在试卷的顶部和底部生成样板 LaTeX。第三行包含我们刚刚创建的问题宏。第四行和第五行包含数据库中的两个问题。

要创建试卷,我们运行命令 m4 exam.m4 > exam.tex。由于 ANSWER_KEY 从未定义,因此每个问题都将包含用于答案的空间。要创建答案键,我们使用 m4 的命令行选项来临时定义 ANSWER_KEY 宏

m4 -DANSWER_KEY exam.m4 > exam.key.tex
结论

m4 是一种工具,在无数领域都有应用。在任何您想减少重复工作的地方,m4 都可以提供帮助。它的功能非常丰富,您可以完成几乎任何事情并生成您可能希望的任何类型的内容。我希望我已经给了您足够的东西来激发您对 m4 的兴趣,并让您了解 m4 可以为您做什么。祝您宏编写愉快!

资源

The m4 Macro Package
电子邮件:adams@csis.gvsu.edu

Robert Adams 是 GVSU 的计算机科学教授。当他不教书时,他喜欢拉小提琴、跳舞和与女儿 Turah 共度时光。

加载 Disqus 评论