用m4 macros创建文本文件

ArticleCategory:

Webdesign

AuthorImage:

[Photo de l'auteur]

TranslationInfo:

Original in fr John Perr

fr to en:John Perr

AboutTheAuthor:

从1994年便成为了Linux 的使用者.他是 LinuxFocus 的法国成员之一.


Abstract:

这一教程描述了如何用m4宏处理器简化html文件的维护.


ArticleIllustration:

[Illustration]

ArticleBody:

介绍

宏命令语言经常被用于文本编辑器.而多数的文本编辑器已经拥有这样的语言功能.C语言的编译器也为编程者在编译预处理时提供了这一特性.而在维护一些配置文件和管理小型网站时,GNU/m4 宏处理器可以有效的减轻工作量.GNU/m4 宏处理器伴随所有的Linux版本发行,成为所有Unix使用者的一个标准.

在下面,我们将向你展示如何运用GNU/m4 宏处理器去维护一个小型网站的HTML页面.这个程序将帮助我们确保整个网站的连贯性.当然,对于此我们可以采用多种系统工具实现---那也正是Unix的魅力之所在.

这种技术已经应用于我们所熟悉的sendmail.那是因为使用了由Eric Allman所设计的m4 宏组件工具.

GNU/m4 宏处理器并不只局限于文本和HTML文件的编辑,对于想要扩展CPP特性或是想要以其他语言实现与CPP特性同等效果的程序设计者时非常有用的.

定义

宏处理器是一个解释用户定义的命令的程序,宏经常使用于文本管理.如下定义:

define(AUTHOR,`Agatha Christiea.christie@scotland-yard.gov')

被允许在文本的任何地方使用 "AUTHOR"这个词.并且在m4处理后,他将被替换为"Agatha Christie<a.christie@scotland-yard.gov>".当然他还会有很多其它的功能,我们将在下面展示.

一个例子

我们假设我们将要维护一个拥有相同页面但却是不同的语言的网站.而且每一个页面都拥有相同的header和footer以实现页面外观的统一.为了简化并且避免使用浏览器来测试我们的结果,我们的例子将字处理文本内容.这样也同时使得人们只使用lynx这样的工具就可以访问我们的网站.以下是其中一页的HTML代码:

HTML文本

  
<!-- Start of header -->
<HTML>
<HEAD>
<TITLE>Lynx homepage</TITLE>
<META name="description" content="Site lynx et m4"> 
</HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#008000" VLINK="#808080" ALINK="#8080FF">
<TABLE>
  <TBODY>
  <TR><TD align=middle colspan="2">
      <H1>Lynx a fully-featured World Wide Web client for character-cell
displays</H1>
  <TR><TD align="left" valign="top" width="15%">
      <a href="./index-en.html">English</A><BR>
      <a href="./index-fr.html">French</A><BR>
      <a href="./index-es.html">Italian</A><BR>
      <a href="./index-it.html">Spanish</A><BR>
      <a href="./index-de.html">German</A><BR>
      <TD align=left>
<!-- End of header -->

      <P>Links to the current sources and support materials for Lynx are
   maintained at <A HREF="http://www.crl.com/~subir/lynx.html">Lynx
   links</A></P>
      <P> and at the Lynx homepage
      <A HREF="http://lynx.browser.org/">Lynx
      Information.</A></P>
      <P>View these pages for information about Lynx, including new
      updates.</P>

      <P>Lynx is distributed under the GNU General Public License (GPL) without
   restrictions on usage or redistribution.  The Lynx copyright statement,
   "COPYHEADER", and GNU GPL, "COPYING", are included in the top-level
   directory of the distribution.  Lynx is supported by the Lynx user
   community, an entirely volunteer (and unofficial) organization.</P>

<!-- Start of footer -->
</TBODY>
</TABLE>
<HR size="0" noshadow>
<FONT SIZE=-2>
<EM>Page maintained by John Perr.<BR>
Page updated on 25/07/99
- © <A HREF="mailto:webmaster@lynx.browser.org">lynx.browser.org</A>1999
</EM></FONT>
</BODY>
</HTML>
<!-- End of footer -->

以下是浏览的结果:

[Click here to enlarge image] [Click here to enlarge image]
用 lynx 用 netscape

所有的页面都拥有相同的header和footer式样,只是语言和页面的body部分不同.我们现在将要开始编辑m4宏,他们将被加入我们的HTML文本以替换所有重复性的数据.
在我们接触宏的细节之前,让我们上边这个用到宏的例子:

宏文本:

  
LYNX_TITRE(Lynx a fully-featured World Wide Web
            client for character-cell displays)
LYNX_ENTETE(Lynx homepage)

      <P>Links to the current sources and support materials
      for Lynx are maintained at
      <A HREF="http://www.crl.com/~subir/lynx.html">
      Lynx links</A></P>
      <P> and at the Lynx homepage
      <A HREF="http://lynx.browser.org/">
      Lynx Information.</A></P>
      <P>View these pages for information about Lynx,
      including new updates.</P>

      <P>Lynx is distributed under the
      GNU General Public License (GPL) without
   restrictions on usage or redistribution.
   The Lynx copyright statement, "COPYHEADER",
   and GNU GPL, "COPYING", are included in the top-level
   directory of the distribution.
   Lynx is supported by the Lynx user community,
   an entirely volunteer (and unofficial) organization.</P>
LYNX_PIED

像这样,编辑HTML页面比较容易并且不会造成HTML标示之间的文本丢失.As such, writing HTML pages is simpler and the text is not lost among HTML tags. 对于其他语言,比喻进行翻译.法语版将变成:

  
LYNX_TITRE(Lynx un navigateur en mode console)
LYNX_ENTETE(Un site pour les utilisateurs de lynx)

   <P>Visitez le
   <A HREF="http://lynx.browser.org/">
   site officiel de lynx</A>
   pour plus d'informations sur Lynx,
   y compris les nouvelles mises ?jour.</P>
       
   <P>Les liens vers les sources de la version
   courante et divers supports pour Lynx sont
   tenus ?jour sur le site
   <A HREF="http://www.crl.com/~subir/lynx.html">
   liens Lynx</A>.</P>

   <P>Lynx est distribue dans le cadre de la lisence GNU
   (General Public License - GPL)
   sans restriction sur son utilisation ni sa distribution.
   Les mentions des droits de reproduction de Lynx, "COPYHEADER",
   et GNU GPL, "COPYING", sont inclus dans la racine de
   l'arborescence de la distribution. Lynx est supporte par
   la communaute des utilisateurs de Lynx, une communaute
   enti鑢ement benevole (et non-officielle).</P>
LYNX_PIED

对于每一种语言,相同的宏LYNX_TITRE, LYNX_ENTETE 和 LYNX_PIED 分别携带不同的参数被使用.这三个宏可以实现对HTML文本中的header和footer高效替换.这是这一系统的主要优点:header和footer的定义对于整个站点来说是一致的.假如header和footer的内容需要改变的话,只需要修改宏的定义文件而不再需要修改所有的页面了.

宏的定义

以上为了达到相同的格式我们定义了三个宏.这里是宏的定义文件,内容如下:

  
divert(-1)
# File mac.css
# Version 1.0 M4 macros for Lynx
#
# A file trans-LANG.m4 is defined for each
# language, based on the french one.
# If no translation file exist,
# french is the default.
#
divert(0)
changequote({,})dnl # change quotes to curly braces
ifdef({LANG},,{define({LANG},{fr})})dnl # Default= french
include({trans-}LANG{.m4})dnl	  # call translation file
undefine({format})dnl		  # Suppress the format definition
define({_ANNEE_},esyscmd(date +%Y))dnl 	  #Current year
define({LYNX_TITRE},{define(_TITLE_,$1)})dnl # First macro
dnl # Second macro
define({LYNX_ENTETE},{<!-- Header start -->
<HTML>
<HEAD>
<TITLE>$1</TITLE>
<META name="description" content="Site lynx and m4"> 
<META name="keywords" content="m4, lynx, GPL">
</HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#008000" VLINK="#808080" ALINK="#8080FF">
<TABLE>
  <TBODY>
  <TR><TD align=middle colspan="2">
      <H1>_TITLE_</H1>
  <TR><TD align="left" valign="top" width="15%">
      <a href="./index-en.html">_ANGLAIS_</A><BR>
      <a href="./index-fr.html">_FRANCAIS_</A><BR>
      <a href="./index-es.html">_ESPAGNOL_</A><BR>
      <a href="./index-it.html">_ITALIEN_</A><BR>
      <a href="./index-de.html">_ALLEMAND_</A><BR>
      <TD align=left>
<!-- end of header -->})dnl
dnl # Third macro
define({LYNX_PIED},{<!-- Start of footer -->
</TBODY>
</TABLE>
<HR size="0" noshadow>
<FONT SIZE=-2>
<EM>_MAINTENEUR_.<BR>
_MAJ_
esyscmd(date +%d/%m/%y)
- &copy <A HREF="mailto:webmaster@lynx.browser.org">
lynx.browser.org</A>
_ANNEE_</EM></FONT>
</BODY>
</HTML>
<!-- End of footer -->})dnl

解释

在"divert(-1)"和"divert(0)"之间的内容是注释,"Divert"是m4处理器的内置宏.他指向处理程序的输出.使用参数-1,告诉处理程序不向最终的HTML文件写入下面的内容,这也是我们所希望的.

宏"changequote"重新定义经常被引用的宏参数.他们将在此被替换为花括号({}),这是因为在文本中,特别是法语文本中这种引用会被广泛的使用而与宏之间产生误解.花括号({})在文本或是HTML文件中很少使用,这也是为什么我们选择了花括号({}).

宏"ifdef"用于测试宏LANG是否已经被定义了并且将他置为"fr".宏LANG被用于设定语言.在以下的几行中,我们将看到为了选择HTML页面的语言,如何在呼叫m4的时候定义.

与在C语言中的意义相同include开头的几行由来包含一个外部文件.我们使用它来载入宏所定义的在header和footer中所使用的语言.下面是它的内容所对应的语言:

  
divert(-1)
# File trans-fr.m4
# Definitions for french
divert(0)
define({_ANGLAIS_},{Anglais})dnl
define({_FRANCAIS_},{Fran鏰is})dnl
define({_ITALIEN_},{Espagnol})dnl
define({_ESPAGNOL_},{Italien})dnl
define({_ALLEMAND_},{Allemand})dnl
define({_WEBMASTER_},{John Perr})dnl
define({_MAINTENEUR_},{Page maintenue par _WEBMASTER_})dnl 
define({_MAJ_},{Date de mise à jour:})dnl
  
divert(-1)
# File trans-en.m4
# Definitions for english
divert(0)
define({_ANGLAIS_},{English})dnl
define({_FRANCAIS_},{French})dnl
define({_ITALIEN_},{Spanish})dnl
define({_ESPAGNOL_},{Italian})dnl
define({_ALLEMAND_},{German})dnl
define({_WEBMASTER_},{John Perr})dnl
define({_MAINTENEUR_},{Page maintained by _WEBMASTER_})dnl
define({_MAJ_},{Page updated on })dnl

如果你使用的是西班牙语,意大利语和德语,你可以写出相识的对应于这些语言的文件.

"undefine"禁止了在这里不用的内置的"format".除非已经被引用(用{}包围起来)如果这一行被省略,在这个文本文件中"format"每次出现时将会消失.在设计一个小型网站时,这样做是不可取的.

接下来是对现在所在的年份的定义,这是由宏"easyscmd"所得到的,"easyscmd"呼叫Unix的系统命令date.这个命令也在需要在header和footer中打印出页面更新日期的时候被使用.

接下来的一行定义了我们的主要的三个宏中的第一个: LYNX_TITRE.这个宏定义了另外一个叫 _TITRE_ 的宏.为了在页面的header和footer中多次使用一个声明这是必要的.请注意 $1 的作用是指向宏的第一个参数.

剩下的段落是定义另外两个主要的宏:LYNX_ENTETE 和 LYNX_PIED.他们分别对应于页面中(除变量)header和footer中的内容.它们是:

在这一行末尾所出现的"dnl"是一个m4的内置的宏,意思是"删除至新的一行".有了 "dnl" m4就不会在解释宏的时候产生一个空行.

创建页面

现在我们的系统已经设置完毕,下面的命令将从这些文件生成主页页面.

"XX" 是对于各种语言的代码.请注意这里应用了选项 -D ,类适于C语言的gcc,从命令行定义了一个宏.

总结

下表提供了在这个例子中的文件和它们的作用.

下面的文件用于生成HTML页面:

index-XX.html 页面的主体,这是由作者或是由翻译者所写的.对于每个页面和每种语言其内容是不同的(对于英语代码是 XX=en ,对于西班牙语代码是 XX=es 等等).
mac.css 包含了标准的定义.对于所有的页面,所有的语言这个文件是共同的.可以被看作是定义了某种页面风格的文件.
trans-XX.m4 对每一种语言的标准定义.这个文件对于一种语言的所有页面是共同的.(对于英语代码是 XX=en ,对于西班牙语代码是 XX=es 等等).

结论

尽管它的作用很明显,m4处理程序不可能与其他的脚本语言相比,如Perl Tcl.但一旦你了解到它的特性,便是一个处理文本文件快速而且唾手可得的工具.你可以从操作系统的文档中得到更多的参考.你可以找到一个大约30页的m4教材,其中包括了GNU/m4宏处理程序的所有方面.你同时也可以访问一下波尔多Linux俱乐部(ABUL),这个站点就是用m4宏进行维护的,与我们今天的例子非常相识.

连接

可以从 ftp://prep.ai.mit.edu/pub/gnu/m4-1.4.tar.gz 得到GNU/m4
下载以上的文件: The Lynx m4 macro kit

感谢 Paul Kienzle 审阅这篇文章.