m4 宏处理器概述

本主题提供有关 m4 宏处理器的信息,此处理器是在操作系统环境中使用的任何编程语言的前端处理机。

在程序开始处,您可以定义符号名称或者符号常量作为特定的字符串。 然后,可以使用 m4 宏处理器将未加引号的符号名称替换为相应的字符串。 除了将一个文本字符串替换为另一个, m4 宏处理器还提供下面的功能:

  • 计算功能
  • 文件操作
  • 有条件的宏扩展
  • 字符串和子串功能

m4 宏处理器处理字母字符串和称为标记的数字。 m4 宏处理器读出每个字母数字标记并确定它是否为宏名称。 然后,程序将宏名称替换为其定义文本,并将结果字符串推送回输入以重新扫描。 您可以带参数调用宏,这种情况下,在重新扫描定义文本之前,会收集参数并将它们替换到定义文本中的正确位置。

m4 宏处理器提供内置宏,例如:define。 您也可以创建新的宏。 内置宏和用户定义的宏以相同的方式工作。

使用 m4 宏处理器

要使用 m4 宏处理器,请输入下面的命令:

m4 [file]

m4 宏处理器按顺序处理每个参数。 如果没有参数或者参数为 -(短划线),那么 m4 宏处理器读入标准输入作为其输入文件。 m4 宏处理器将其结果写入标准输出。 因此,要将输出重定向到文件供以后使用,请使用以下命令:

m4 [file] >outputfile

创建用户定义的宏

描述
定义宏名称, 更换) 定义值为 Replacement 的新的宏 MacroName

例如,如果下面的语句出现在程序中:

define(name, stuff)

m4 宏处理器将字符串 name 定义为stuff.在程序文件中出现字符串 name 时, m4 宏处理器会将其替换为字符串stuff.字符串name必须是 ASCII 字母数字,并且必须以字母或下划线开头。 字符串stuff是任何文本,但如果文本包含括号,那么开括号或左括号的数目必须等于闭括号或右括号的数目。 使用 / (斜杠) 字符来传播文本stuff多行。

开括号(左括号)必须紧跟在 define 一词之后。 例如:

define(N, 100)
 . . . 
if (i > N)

定义N100并在稍后的 if 语句中使用符号常量 N。

程序中的宏调用具有以下格式:

name(arg1,arg2, . . . argn)
仅当宏名称两边为非字母数字字符时才能被识别。 在以下示例中,变量NNN与定义的宏无关N.
define(N, 100)
 . . . 
if (NNN > 100)

您可以其他的名称定义宏。 例如:

define(N, 100)
define(M, N)

定义两者MN100. 如果您稍后更改了N并为其分配新值,M保留值100,不N.

m4 宏处理器会尽快将宏名称扩展为其定义文本。 字符串N替换为100. 然后是字符串M也替换为100. 总体结果与最初使用以下输入相同。

define(M, 100)

定义的顺序可如下被交换:

define(M, N)
define(N, 100)

现在M定义为字符串N,所以当M的值,结果为N当时 (因为 M 被替换为N,替换为100).

使用引号字符

要延迟参数 define 的扩展,请将它们括在引号字符中。 如果您不更改它们,引号字符为‘ 和 ’(左右单引号)。 被引号字符括起来的文本不会立即被扩展,但是会除去引号字符。 加了引号的字符串的值为除去引号字符的字符串。 如果输入为:

define(N, 100)
define(M, `N')

周围的引号字符N将在收集参数时除去。 使用引号字符的结果是定义M作为字符串N,not100. 一般规则是, m4 宏处理器总是在评估某项内容时删除一个级别的引号字符。 即使在宏之外也是如此。 让这个词define显示在输出中,输入引号字符中的单词,如下所示:

`define' = 1;

另一个使用引号字符的示例是重新定义N. 重新定义N,通过放置来延迟评估N在引号字符中。 例如:

define(N, 100)
. . . 
define(`N', 200)

要防止出现问题,请将宏的第一个参数加引号。 例如,以下片段不会重新定义N:

define(N, 100)
. . . 
define(N, 200)

N在第二个定义中,替换为 100。 结果与下面的语句相同:

define(100, 200)

m4 宏处理器忽略此语句,因为它只能定义名称而不是数字。

更改引号字符

引号字符通常为‘ 和 ’(左右单引号)。 如果那些字符不方便,请使用下面的内置宏更改引号字符:

描述
changequote (l r ) 将左和右引号字符更改为 lr 变量所表示的字符。

要恢复原来的引号字符,请如下使用不带参数的 changequote

changequote

Arguments

最简单的宏处理格式是用将一个字符串替换为另一个(固定的)字符串。 但是,宏也可有参数,所以您能在不同的地方使用宏得到不同的结果。 要指示在宏的替换文本中的何处使用参数(其定义的第二个参数),请使用符号 $n 指示第 n 个参数。 使用宏时,m4 宏处理器将记号替换为所指示的参数的值。 例如,下面的符号:

$2

是指宏的第二个参数。 因此,如果您定义了一个名为bump作为:

define(bump, $1 = $1 + 1)

m4 宏处理器生成代码以通过 1 递增第一个自变量。 该bump(x)语句等效于 x = x + 1。

宏可有任意所需数目的参数。 但是,您只能使用 $n 符号 ($1认知驱动$9)。 要访问超过第九个自变量的自变量,请使用 shift 宏。

描述
shift (ParameterList) 返回 ParameterList 中除了第一个元素之外的所有元素,以执行列表的破坏性左移。

此宏删除第一个自变量,并将其余自变量重新分配给 $n 个符号 (第二个自变量$1,第三个自变量$2... 第十个自变量$9)。 多次使用 shift 宏允许访问与宏一起使用的所有参数。

$0 宏返回宏名称。 未提供的参数用空字符串替换,所以您可以如下定义连接其参数的宏:

define(cat, $1$2$3$4$5$6$7$8$9)

因此:

cat(x, y, z)

等同于:

xyz

Arguments$4认知驱动$9在此示例中为空,因为未提供相应的自变量。

m4 宏处理器废弃参数中开头的不加引号的空格、制表符或者换行符,但是保留所有其他空格。 因此:

define(a, b c)

定义ab c.

参数用逗号分开。 使用括号将包含逗号的参数括起来,这样逗号就不会结束参数。 例如:

define(a, (b,c))

仅有两个参数。 第一个参数是a,二是 (b,c)。 要使用逗号或单括号,请将其括在引号字符中。

使用预定义的 m4 宏

m4 宏处理器提供了一组预定义的宏。 本节解释了很多宏及其用法。

除去宏定义

描述
undefine (`MacroName') 除去用户定义的或者内置宏的定义 (`MacroName')

例如:

undefine(`N')

除去 N 的定义。 除去具有 undefine 宏的内置宏后,如下所示:

undefine(`define')

您就不能再使用内置宏的定义。

在此情况下,需要单引号防止替换。

检查已定义的宏

描述
ifdef (`MacroName', Argument1, Argument2) 如果宏 MacroName 已定义而且未被定义为零,那么返回 Argument1 的值。 否则,它将返回 Argument2

ifdef 宏允许三个参数。 如果定义了第一个自变量,那么 ifdef 的值是第二个自变量。 如果未定义第一个自变量,那么 ifdef 的值是第三个自变量。 如果没有第三个参数,那么 ifdef 的值为空。

使用整数运算

m4 宏处理器提供下面的内置函数仅对整数进行运算:

描述
incr (Number) 返回 Number + 1 的值。
decr (Number) 返回 Number - 1 的值。
eval 对算术表达式进行求值。

那么,要将变量定义为大于 Number 值的变量,请使用以下行:

define(Number, 100)
define(Number1, `incr(Number)')

这定义了Number1作为比当前值多 1 个Number.

eval 函数能对包含以下运算符(按照优先顺序的降序排列)的表达式进行求值:
  • 一元 +-
  • **^ (指数)
  • */% (模数)
  • + -
  • == != < <= > >=
  • !(非)
  • & && (逻辑 AND)
  • | | | (逻辑 OR)

使用括号在需要的地方将操作分组。 表达式的所有操作数必须是数字。 true 关系 (例如, 1> 0) 的数字值为 1 , false 为 0。 eval 函数的精度为 32 位。

例如,定义M2==N+1使用 eval 函数,如下所示:

define(N, 3)
define(M, `eval(2==N+1)')

除非文本十分简单,否则请使用引号字符将定义宏的文本括起来

操作文件

要将新文件合并到输入中,请使用内置 include 函数。

描述
include (文件) 返回文件 File 的内容。

例如:

include(FileName)

插入内容FileName以代替 include 命令。

如果 include 宏中命名的文件无法访问,那么会出现致命错误。 要避免致命错误,请使用备用格式:sinclude 宏(静态包含)。

描述
sinclude (文件 ) 返回文件 File 的内容,但是如果它无法访问 File,那么不会报告错误。

sinclude(静态包含)宏不会写消息,但是如果无法访问命名的文件则将继续。

重定向输出

m4 宏处理器的输出在处理期间可以再次被重定向到临时文件,而且收集的资料可以根据命令被输出。 m4 宏处理器维护 9 个可能的临时文件,编号为 1 到 9。 如果使用内置 转移 宏。

描述
转移 (Number) 将输出流更改到临时文件 Number

m4 宏处理器在临时文件 Number末尾的 转移 函数之后写入程序的所有输出。 要将输出返回到显示屏幕,请使用 divert 或者 divert(0) 函数,此函数将继续正常地输出过程。

m4 宏处理器在处理结束时将所有的已重定向输出按照数字顺序写到临时文件中。 如果您将输出重定向到除了 0 到 9 之外的其他临时文件,那么 m4 宏处理器会废弃输出。

要按照数字顺序从所有的临时文件中将数据取回,请使用内置 undivert 宏。

描述
取消转移 (Number1 Number2... ) 将指示的临时文件的内容追加到当前临时文件中。

要按照指定的顺序取回选定的临时文件,请使用带参数的内置 undivert 宏。 使用 undivert 宏时,m4 宏处理器将废弃已恢复的临时文件,并且不会搜索宏的已恢复数据。

undivert 宏的值不是已转向的文本。

您可以使用 divnum 宏来确定当前正在使用的临时文件。

描述
divnum 返回当前活动的临时文件的值。

如果您不使用 divert 宏更改输出文件,那么 m4 宏处理器会将所有的输出放到名为 0 的临时文件中。

在程序中使用系统程序

您可以使用内置 syscmd 宏从程序中将任何程序运行在操作系统中。 例如,下面的语句运行 date 程序:

syscmd(date)

使用唯一文件名

使用内置 maketemp 宏从程序中得到唯一的文件名。

描述
maketemp (字符串 ... nnnnn ...字符串) 通过将自变量字符串中的字符 nnnnn 替换为当前进程标识来创建唯一的文件名。

例如,对于语句:

maketemp(myfilennnnn)

m4 宏处理器返回一个字符串,myfile与进程标识并置。 使用此字符串命名临时文件。

使用条件表达式

条件表达式求值允许确定宏表达式的处理时间。

表达式 描述
ifelse (String1 String2 Argument1 Argument2) 如果 String1String2 匹配,那么将返回 Argument1 的值。 否则它将返回 Argument2

内置 ifelse 宏执行条件测试。 以最简单的格式:

ifelse(a, b, c, d)

比较两个字符串ab.

如果ab完全相同,内置 ifelse 宏将返回字符串c。如果它们不相同,那么将返回字符串d。例如,您可以定义一个名为compare比较两个字符串并返回yes如果它们相同,或no如果它们不同,如下所示:

define(compare, `ifelse($1, $2, yes, no)')

引号字符防止过早对 ifelse 宏求值。 如果没有第四个参数,那么视其为空。

ifelse 宏可以有任意数目的参数,因此提供了格式有限的确定多条路径的能力。 例如:

ifelse(a, b, c, d, e, f, g)

此语句和下面的片段在逻辑上是等同的:

if(a == b) x = c;
else if(d == e) x = f;
else x = g;
return(x);

如果省略最终的参数,那么结果为 null,因此:

ifelse(a, b, c)

cifa匹配b,否则为空。

操作字符串

此部分中的宏允许您将输入字符串转换为输出字符串。

描述
len 返回构成其参数的字符串的字节长度

因此:

len(abcdef)

为 6,且:

len((a,b))

为 5。

描述
DLEN 返回字符串中可显示字符的长度

由 2 字节代码构成的字符被显示为一个字符。 那么,如果字符串包含任何 2 字节全球字符支持的字符,那么 dlen 的结果将与 len 的结果不同。

描述
substr (String Position 长度) 返回 String 的子串,此子串起始于第 Position 个字符,长度为 Length 个字符。

使用输入,substr (s, i, n) 返回 s 的子串,此子串起始于第 i 个位置(起始位置为 0)且长度为 n 个字符。 如果省略 n ,那么返回剩下的字符串。 例如,函数:

substr(`now is the time',1)

返回下面的字符串:

ow is the time
描述
索引 (String1 String2) 返回 String1String2 开始的字符位置从第 0 个字符开始)或者 -1(如果 String1 不包含 String2)。

对于内置 substr 宏,字符串的起点为 0。

描述
转换 (String Set1 Set2) String 中搜索 Set1 中的字符。 如果找到,那么将这些字符更改(直译)为 Set2 中的相应字符。

它的一般格式为:

translit(s, f, t)

修改s替换在以下位置找到的任何字符:f按相应的字符t. 例如,函数:

translit(`little', aeiou, 12345)
将元音替换为相应的数字并返回以下行:
l3ttl2

如果t短于f,没有条目的字符t已删除。 如果t根本不存在,字符来自f删除自s所以:

translit(`little', aeiou)
从字符串中删除元音little并返回以下内容:
lttl
描述
dnl 删除其后的所有字符,直到并包括换行字符。

使用此宏除去空行。 例如,函数:

define(N, 100)
define(M, 200)
define(L, 300)

结果是每一行的末尾为换行,此行并不是定义的一部分。 换行符被传递给输出。 要除去换行符,请在每行添加内置 dnl 宏。

define(N, 100) dnl
define(M, 200) dnl
define(L, 300) dnl

调试 M4 宏

此部分中的宏允许您报告错误和处理信息。

描述
errprint (字符串) 将参数 (String) 写到标准错误文件

例如:

errprint (`error')
描述
dumpdef (`MacroName'...) 转储命名为参数 (`MacroName'...) 的项的当前名称和定义

如果您不提供参数,那么 dumpdef 宏显示所有当前名称和定义。 请记住给名称加引号。

其他 m4 宏

其他 m4 宏以及每个宏的简要说明的列表如下:

描述
changecom (l r ) 将左和右注释字符更改为 lr 变量所表示的字符。
defn (MacroName) 返回括起来的 MacroName 的定义
en (字符串) 返回 String 中的字符数。
m4exit (代码) 退出 m4 宏处理器,返回码为 Code
m4wrap (MacroName) m4 宏处理器末尾运行宏 MacroName
popdef (MacroName) MacroName 的当前定义替换为保存在pushdef 宏中的先前的定义。
pushdef (宏名称, 更换) 保存 MacroName 的当前定义,然后将 MacroName 定义为 Replacement
系统值 获取 syscmd 宏的最后使用处的返回码。
跟踪 (MacroList) 关闭 MacroList 中对任何宏的跟踪。 如果 MacroList 为 null,那么关闭所有的跟踪。
traceon (MacroName) 开启 MacroName 宏的跟踪。 如果 MacroName 为 null,那么开启所有宏的跟踪。