找回密码
 注册
查看: 701|回复: 2

转贴几个基础知识

[复制链接]
发表于 2005-11-1 14:28:20 | 显示全部楼层 |阅读模式
小鸟学习 老鸟温故

一、用户登陆进入系统后的系统环境变量:
$HOME 使用者自己的目录
$PATH 执行命令时所搜寻的目录
$TZ 时区
$MAILCHECK 每隔多少秒检查是否有新的信件
$PS1 在命令列时的提示号
$PS2 当命令尚未打完时,Shell 要求再输入时的提示号
$MANPATH man 指令的搜寻路径

二、特殊变量:

$0 这个程序的执行名字
$n 这个程序的第n个参数值,n=1..9
$* 这个程序的所有参数
$# 这个程序的参数个数
$$ 这个程序的PID
$! 执行上一个指令的PID
$? 执行上一个指令的返回值

三、shell中的变元:
* 任意字符串
? 一个任意字符
[abc] a, b, c三者中之一
[a-n] 从a到n的任一字符

四、几个特殊字符表示

\b 退回
\c 打印一行时没有换行符 这个我们经常会用到
\f 换页
\r 回车
\t 制表
\v 垂直制表
\\ 反斜线本身

五、判断文件的属性

格式:-操作符 filename
-e 文件存在返回1, 否则返回0
-r 文件可读返回1,否则返回0
-w 文件可写返回1,否则返回0
-x 文件可执行返回1,否则返回0
-o 文件属于用户本人返回1, 否则返回0
-z 文件长度为0返回1, 否则返回0.
-f 文件为普通文件返回1, 否则返回0
-d 文件为目录文件时返回1, 否则返回0

六、测试字符串
字符串1 = 字符串2 当两个字串相等时为真
字符串1 != 字符串2 当两个字串不等时为真
-n 字符串      当字符串的长度大于0时为真
-z 字符串      当字符串的长度为0时为真
字符串       当串字符串为非空时为真

七、测试两个整数关系
数字1 -eq 数字2     两数相等为真
数字1 -ne 数字2     两数不等为真
数字1 -gt 数字2     数字1大于数字2为真
数字1 -ge 数字2     数字1大于等于数字2为真
数字1 -lt 数字2     数字1小于数字2为真
数字1 -le 数字2     数字1小于等于数字2为真

八、逻辑测试
-a         与
-o        或
!        非



今天介绍shell特殊字符的引用
===============================
shell中的特殊字符有

1、$ 美元符
2、\ 反斜杠
3、` 反引号
4、" 双引号
5、< ,>;,*,?,[,]

下面我一一举列说明
一、$符号
1、echo $? 显示的是上一条指令退出状态
2、echo "$?" 效果同上
3、echo '$?' 显示的是$?
4、echo \$? 显示的是$?
5、echo "\$?" 显示的是$?

  大家可能已经看出 $符号在双引号中具有特殊意义 双引号对$符号不起作用
而单引号可以将特殊字符的的特殊意义屏蔽掉,使其能显示为字符本身,反斜
杠也可以将特殊字符的特殊含义屏蔽掉,使特殊字符失去特殊含义。

二、\ 反斜杠
  反斜杠的作用是将特殊符号字符的特殊含义屏蔽掉,使其还是原字符
A=1234
echo \$A 显示为$A 如果不加\将显示为1234
echo \` 显示为`
echo \" 显示为双引号
echo \\ 显示为\

三、` 反引号
  反引号的功能是命令替换,将反引号中的字符串做为命令来执行,我们在用shell编程时经常用的到 将系统命令的执行结果赋给一个变量

A=`date`
echo $A 显示的不是date而是当时的时间串
比如有一文件A的内容如下 
ABCDEFG
1234456
abcdefg

B=`cat A|grep 234`? # 检索文件A中含有字符串234的行
echo $B 将显示为1234456
echo "$B" 将显示为什么?
echo "\$B" 将显示为什么?读者自己试试

四、" 双引号
  在系统中有些特殊字符,为避免引用这些特殊字符 往往用双引号或单引号将这些特殊字符引起来,使其不具有特殊含义。
  但有一部分特殊字符在引号中还是具有特殊含义,用双引号引起来是不起作用的。本文中所列的前四个特殊字符在双引号中还是特殊字符。为了使其不具有特殊含义一是用单引号引进来二是用\反斜线使其失去作用。

  比如我们想原样输出这些特殊字符

echo """
echo "$"
echo "\"
echo "`"
   以上不是你所期望的结果,因为双引号对它们不起作用,你只能这样才能输出这些特殊字符的原形
echo '"'
echo '$'
echo '\'
echo '`'

echo "\""
echo "\$"
echo "\\"
echo "\`"
将分别显示为 " $ \ `
五、其它特殊字符
  大家注意到 除了前四个特殊字符外 我将其它的特殊字符都放在一块,这是因为前四个特殊字符在双引号中还是具有特殊含义,所以单独拿出来讲,除此以外的特殊字符如果你要输出这些特殊字符的原形,你就可以用双引号或单引号引起来使其失去特殊含义。
< ,>;,*,?,[,]对shell有特殊含义 但你可以用双引号引起来输入这些原形

  讲了这么多大家是不是已经注意到所有的特殊字符在单引号中失去特殊含义,如果你要输出特殊字符原形但又记不清那些特殊字符在双引号中不能输出原形,建议你干脆用单引号引起来。

今天介绍条件测试语句

一、if 条件语句 
格式:
if 条件表达式
then #当条件为真时执行以下语句
命令列表
else #为假时执行以下语句
命令列表
fi

if 语句也可以嵌套使用

if 条件表达式1
then
if 条件表达式2
then
命令列表
else
if 条件表达式3
then
命令列表
else
命令列表
fi
fi
else
命令列表
fi

你可以进行多层嵌套 一个if语句一定要跟一个fi 表示该层条件结束  否则会造成语法错误
结合前面讲的 举例如下:
这里先讲一个条件语句中用到的命令test 表示测试test后面的条件是否为真

if test -f "$1"
then
lpr $1
else
if test -d "$1"
then
cd $1
lpr $1
else
echo "$1不是文件或目录"
fi
fi

以上的例子还可以改成如下所示

if test -f "$1"
then
lpr $1
elif test -d "$1" #elif 同else if
then
(cd $1;lpr $1)
else
echo "$1不是文件或目录"
fi??????

以上的例子不知您是否看懂是什么意思吗?
假如我们现在将这个例子保存为prfile
chmod +x prfile
执行刚才的程序
./prfile aaa

这个例子是检查你的输入的参数是否是一个文件 如果是就打印 如果是一个目录 先转目录再打印 如果即不是文件也不是目录给出提示

二、多重条件测试语句case
格式:
case 字串 in
模式) 命令列表;;
模式) 命令列表;;
....
esac

多重条件语句是以case 开始以esac结束 中间可以有多个条件列表 功能是测试字串和和里面的模式有没有匹配的,有就执行里面的命令列表 模式也可以是*号 表示任意字串,每个模式里面的最后要心;;双引号结束,否则会发生语法错误。

现举例如下:

case $1 in
*.c)
cc $1
;;
*.txt)
lpr $1
;;
*)
echo "未知的类型"
esac

假如将以上内容保存在文件abc中

chmod +x abc
执行 ./abc a.c   将会对文件a.c进行编译
执行 ./abc readme.txt 将会把文件通过打印机
假如我将以上内容改一下,你是否会知道它的执行结果?

case $1 in
*)
cc $1
;;
*.txt)
lpr $1
;;
*.c)
echo "未知的类型"
esac

今天介绍循环语句
一. while 循环
while 命令格式

while 条件表
do
命令表
done

执行过程

shell首先执行条件表,如果条件表的最后一条语句的退出状态为零,则执行盾环体内的命令
表,执行完后,再检查条件表,如果退出状态为零将继续执行,如此循环往复直到条件表的
最后一条语句的退出状态非零. 退出状态为零就是条件为真True.

举例说明 假如shell文件的内容如下:

Sum=0
i=0
while true #true是系统的关键词 表示真
do
i=`expr $i + 1`
Sum=`expr $Sum + $i`
if [ $i = "100" ]
then
break;
fi
done
echo $i $Sum
最后这个程序显示的是 100 5050
这个程序的运算就是将1到100加起来

下面将这个程序再改动一下


Sum=0
i=0
while [ $i != "100" ]
do
i=`expr $i + 1`
Sum=`expr $Sum + $i`
done
echo $i $Sum

改动后的程序运算结果和上面是一样 但程序比上面的要简练

在这个循环中还可以以until做为测试条件 它正好与while测试的条件相反,也就是当条件为假时将继续执行循环体内的语句,否则就退出循环体,下面还用这个例子.


Sum=0
i=0
until [ $i = "100" ]
do
i=`expr $i + 1`
Sum=`expr $Sum + $i`
done
echo $i $Sum
当i不等于100时循环 就是当条件为假时循环,否则就退出,而第一个例子是当i不等于100
时循环,也就是测试条件为真时循环.

二.for 循环

命令格式:
for 变量 in 名字列表
do
命令列表
done

这里的名字列表是一个由空格分隔的字符串列表,shell在执行for循环时每次依次从名字表
中取出一个字符串赋给循环变量作为变量的值.
在写for语句时,也可以省略in 名字列表部分,这表示用当前的位置参数来代替这时的名
字列表.
下面举个例子
比如在你的电脑中有两个目录,一个是aa,一个是bb在这两个目录中有5个相同的文件,但其
中一个目录中的一个或多个文件刚刚修改过,现在我忘记刚才改的是那几个文件 了,那么我靠梢员冉弦幌抡饬礁瞿柯嫉奈募?椭?懒?程序如下:

for File in a1 a2 a3 a4 a5
do
diff aa/$File bb/$File
done

下面再举一个不带名字列表的例子

for File
do
echo $Filw
done

文件内容保存在a.sh中 并可执行
我们在执行这个shell程序时命令行如下:
a.sh a1 a2 a3 a4 a5
执行结果如下:
a1
a2
a3
a4
a5
大家从这个例子中可以看到命令行的参数被逐一读入一次
三.循环控制语句
break 命令不执行当前循环体内break下面的语句从当前循环退出.
continue 命令是程序在本循体内忽略下面的语句,从循环头开始执行.

一,命令组合:圆括号和花括号
shell中有两种方法将命令组合在一起:圆括号和花括号.圆括号使shell创建一个子shell
来读取并执行括起来的名命令.左括号和右括号不论出现在命令行中的什么位置,shell都会
认为它们具有特殊的组合意义的.只有用双引号将它们括起来引用,才表示圆括号或花括号
的原义.例如:

echo a(b)
将出现语法上的错误,要想输出a(b)字符串 只能括起来
echo "a(b)"
或echo a"("b""
这样才能被shell正确解释.
利用组合命令有什么作用呢?
一,用圆括号组合命令
圆括号的组合命令可以创建子进程运行组合程序,建立子进程的功能是很有用的,因为
子shell在组合命令中的种种操作都不会影响到当前shell的各变量的值.
例如:
子进程在执行组合命令时改变了工作目录,并在新的工作目录下执行一系例命令,执行
完后它可以不必返回原工作目录,因为子进程工作目录的改变不会影响到当前工作目录.

创建子进程后将当前的环境也同样传给子shell,当前shell中用export输出到环境中的
各变量在子shell中同样有效.


花括号也可以将命令组合在一起.左 右花括号只有作为一条命令的第一个字出现时,
shell才它们含有特殊含义.
与圆括号不同的是花括号并不创建子shell,只是由当前的shell来读取并执行括起来的
命令.有时用户希望使用一组命令的顺序输出作为另一组命令的输入,此时用花括号是很方
便的.
不论是用圆括号不是花括号,退出状态都是等于最后一条括起来的命令的退出状态.


二,可以在当前shell中执行的命令

用户在使用shell时一定要了解那些是可以在当前shell中执行的命令 那些不可以
可以在当前shell中执行的命令有:

break case cd continue
echo eval exec exit
export for if read
readonly return set shift
test times trap umask
until wait while
: {}

-----------------------------------
[quote]前言
Preface

有关本手册 :
        这是一本AWK学习指引, 其重点着重于 :   
           AWK 适于解决哪些问题 ?
           AWK 常见的解题模式为何  ?
为使读者快速掌握AWK解题的模式及特性, 本手册系由一些较具
代表性的范例及其题解所构成; 各范例由浅入深, 彼此间相互连贯,
范例中并对所使用的AWK语法及指令辅以必要的说明. 有关AWK的
指令, 函数,...等条列式的说明则收录于附录中, 以利读者往后撰写
程序时查阅. 如此编排, 可让读者在短时间内顺畅地学会使用AWK
来解决问题. 建议读者循着范例上机实习, 以加深学习效果.


读者宜先具备下列背景 :
[a.] UNIX 环境下的简单操作及基本概念.
      例如 : 档案编辑, 档案复制 及 pipe, I/O Redirection 等概念

[b.] C 语言的基本语法及流程控制指令.
(AWK 指令并不多, 且其中之大部分与 C语言中之用法一致, 本手册
中对该类指令之语法及特性不再加以繁冗的说明, 读者若欲深究,
可自行翻阅相关的 C 语言书籍)

Overview of AWK
Why AWK
AWK 是一种程序语言. 它具有一般程序语言常见的功能.
因AWK语言具有某些特点, 如 : 使用直译器(Interpreter)不需先行
编译; 变量无型别之分(Typeless), 可使用文字当数组的注标
(Associative Array)...等特色. 因此, 使用AWK撰写程序比起
使用其它语言更简洁便利且节省时间. AWK还具有一些内建
功能, 使得AWK擅于处理具数据列(Record), 字段(Field)型
态的资料; 此外, AWK内建有pipe的功能, 可将处理中的数据
传送给外部的 Shell命令加以处理, 再将Shell命令处理后的
数据传回AWK程序, 这个特点也使得AWK程序很容易使用
系统资源.
   
由于AWK具有上述特色, 在问题处理的过程, 可轻易使用
AWK来撰写一些小工具; 这些小工具并非用来解决整个大问题,
它们只个别扮演解决问题过程的某些角色, 可藉由Shell所提供的
pipe将数据按需要传送给不同的小工具进行处理, 以解决整个
大问题. 这种解题方式, 使得这些小工具可因不同需求而被重复
组合及使用(reuse); 也可藉此方式来先行测试大程序原型的可行性
与正确性, 将来若需要较高的执行速度时再用C语言来改写.
这是AWK最常被应用之处. 若能常常如此处理问题, 读者可以
以更高的角度来思考抽象的问题, 而不会被拘泥于细节的部份.
本手册为AWK入门的学习指引, 其内容将先强调如何撰写AWK程序,
未列入进一步解题方式的应用实例, 这部分将留待UNIX进阶手册中
再行讨论.
如何取得 AWK

一般的UNIX操作系统, 本身即附有AWK. 不同的UNIX操作系统
所附的AWK其版本亦不尽相同. 若读者所使用的系统上未附有AWK,
可透过 anonymous ftp 到下列地方取得 :
         phi.sinica.edu.tw:/pub/gnu
         ftp.edu.tw:/UNIX/gnu
         prep.ai.mit.edu:/pub/gnu

--------------------------------------------------------------------------------

How AWK works

为便于解释AWK程序架构, 及有关术语(terminology), 先以一个
员工薪资档(emp.dat ), 来加以介绍.

         A125  & Jenny  &100  &210   
         A341  & Dan    &110  &215
         P158  & Max    &130  &209
         P148  & John   &125  &220
         A123  & Linda  & 95  &210      
档案中各字段依次为 员工ID, 姓名, 薪资率,及 实际工时. ID
中的第一码为部门识别码. ``A'',''P''分别表示``组装''及``包装''部门.
本小节着重于说明AWK程序的主要架构及工作原理, 并对一些重要
的名词辅以必要的解 释. 由这部分内容, 读者可体会出AWK语言
的主要精神及AWK与其它语程序言的差异处. 为便于说明, 以条列
方式说明于后.

名词定义

资料列: AWK从数据文件上读取数据的基本单位.以上列档案
emp.dat为例, AWK读入的
   第一笔资料列是 "A125   Jenny  100  210"
   第二笔资料列是 "A341   Dan    110  215"
   一般而言, 一笔数据列相当于数据文件上的一行资料.
   (参考 : 附录 B 内建变数``RS'' )

字段(Field) : 为资料列上被分隔开的子字符串.
          以资料列``A125   Jenny  100  210''为例,
第一栏 第二栏  第三栏 第四栏  ``A125''  ``Jenny''  100  210  
一般是以空格符来分隔相邻的字段. ( 参考 : 附录 D  内建
变数``FS'' )

如何执行AWK
于UNIX的命令列上键入诸如下列格式的指令: ( ``$''表Shell命令
列上的提示符号)
$awk 'AWK程序'  数据文件文件名  
则AWK会先编译该程序, 然后执行该程序来处理所指定的数据文件.
       (上列方式系直接把程序写在UNIX的命令列上)
AWK程序的主要结构 :
      AWK程序中主要语法是 Pattern  { Actions}, 故常见之AWK
程序其型态如下 :
                Pattern1  { Actions1 }
                Pattern2  { Actions2 }
                ......
                Pattern3  { Actions3 }
   
  Pattern 是什么 ?
AWK 可接受许多不同型态的 Pattern. 一般常使用 ``关系判断式'
(Relational expres sion) 来当成 Pattern.
例如 :
    x  >; 34 是一个Pattern, 判断变量 x 与 34 是否存在 大于 的关系.

   x == y 是一个Pattern, 判断变量 x 与变量 y 是否存在等于的关系.

    上式中 x  >;34 , x == y 便是典型的Pattern.

    AWK 提供 C 语言中常见的关系操作数(Relational Operators) 如
     >;, <,  >;=,  <=, ==, !=.
    此外, AWK 还提供 ~ (match) 及 !~(not match) 二个关系操作数
(注一). 其用法与涵义如下:
若 A 表一字符串, B 表一 Regular Expression   
        A  ~ B 判断 字符串A 中是否 包含   能合于(match)B式样的
       子字符串.
        A !~ B 判断 字符串A 中是否 未包含 能合于(match)B式样的
       子字符串.
         
        例如 :

  ``banana'' ~ /an/ 整个是一个Pattern.
    因为``banana''中含有可match /an/的子字符串, 故此关系式
    成立(true),
    整个Pattern的值也是true.
相关细节请参考 附录 A Patterns, 附录 E Regular Expression
[注 一 :] 有少数AWK论著, 把 ~, !~ 当成另一类的 Operator,
并不视为一种 Relational Operator. 本手册中将这两个操作数
当成一种 Relational Operator.

Actions 是什么?
      Actions 是由许多AWK指令构成. 而AWK的指令与 C 语言中的
指令十分类似.
        例如 :
AWK的 I/O指令 :  print, printf( ),  getline..
AWK的 流程控制指令 :  if(...){..} else{..},  while(...){...}...

(请参考 附录 B --- ``Actions'' )
   

AWK 如何处理 Pattern { Actions } ?
      AWK 会先判断(Evaluate) 该 Pattern 之值, 若 Pattern 判断
(Evaluate)后之值为true(或不为0的数字,或不是空的字符串), 则 AWK
将执行该 Pattern 所对应的 Actions.
      反之, 若 Pattern 之值不为 true, 则AWK将不执行该 Pattern
      所对应的 Actions.
      例如 :  若AWK程序中有下列两指令


              50   >; 23  :        {print "Hello! The word!!" }
            "banana" ~ /123/  { print "Good morning !" }

AWK会先判断 50  >;23 是否成立. 因为该式成立, 所以AWK将印出
"Hello! The word!!". 而另一 Pattern 为 "banana" ~/123/, 因为
"banana" 内未含有任何子字符串可 match /123/, 该 Pattern 之值为            
false, 故AWK将不会印出 "Good morning !"

   AWK 如何处理{ Actions } 的语法?(缺少Pattern部分)
        有时语法 Pattern { Actions }中, Pattern 部分被省略,
        只剩 {Actions}.
        这种情形表示 ``无条件执行这个 Actions''.  

AWK 的字段变量
AWK 所内建的字段变量及其涵意如下 :
字段变量  涵意  $0 为一字符串, 其内容为目前 AWK 所读入的资料列.  $1 代表 $0 上第一个字段的数据.  $2 代表 $0 上第二栏个位的资料.  ...  其余类推  

读入数据列时, AWK如何修正(update)这些内建的字段变量

当 AWK 从数据文件中读取一笔数据列时, AWK 会使用内建变量
$0 予以记录.
每当 $0 被异动时 (例如 : 读入新的数据列 或 自行变更 $0,...)
      AWK 会立刻重新分析 $0 的字段情况, 并将 $0 上各字段的数据
用 $1, $2, ..予以记录.


AWK的内建变数(Built-in Variables)
AWK 提供了许多内建变量, 使用者于程序中可使用这些变量
来取得相关信息.常见的内建变数有 :
内建变数  涵意  NF (Number of Fields)为一整数, 其值表$0上所存在的字段数目.  NR (Number of Records)为一整数, 其值表AWK已读入的资料列数目.  FILENAMEAWK正在处理的数据文件文件名.  
例如 : AWK 从资料文件 emp.dat 中读入第一笔资料列
  "A125  Jenny  100  210" 之后, 程序中:
   $0  之值将是 "A125   Jenny  100  210"
   $1  之值为 "A125"    $2  之值为 "Jenny"
   $3  之值为 100       $4  之值为 210
   NF 之值为 4         $NF   之值为 210
   NR 之值为 1         FILENAME 之值为 ``emp.dat''

AWK的工作流程 :
       执行AWK时, 它会反复进行下列四步骤.
      
  自动从指定的数据文件中读取一笔数据列.
  自动更新(Update)相关的内建变量之值. 如 :  NF, NR, $0...
  逐次执行程序中 所有 的 Pattern { Actions } 指令.
  当执行完程序中所有 Pattern { Actions } 时, 若数据文件中还
        有未读取的数据, 则反复执行步骤1到步骤4.

        AWK会自动重复进行上述4个步骤, 使用者不须于程序中
撰写这个循环 (Loop).

打印档案中指定的字段数据并加以计算

AWK 处理数据时, 它会自动从数据文件中一次读取一笔记录, 并会
将该数据切分成一个个的字段; 程序中可使用 $1, $2,... 直接取得
各个字段的内容. 这个特色让使用者易于用 AWK 撰写 reformatter
来改变数据格式.

[ 范例 :] 以档案 emp.dat 为例, 计算每人应发工资并打印报表.
[ 分析 :] AWK 会自行一次读入一列数据, 故程序中仅需告诉
AWK 如何处理所读入的数据列.

      执行如下命令 : ( $ 表UNIX命令列上的提示符号 )
      
        awk '{ print  $2, $3 * $4  }' emp.dat
     执行结果如下 :
     屏幕出现  :

                 Jenny 21000
                 Dan 23650
                 Max 27170
                 John 27500
                 Linda 19950


     说 明 :  
        
UNIX命令列上, 执行AWK的语法为:
      awk  'AWK程序'  欲处理的资料文件文件名.
      本范例中的 程序部分 为 {print $2, $3 * $4}.
      把程序置于命令列时, 程序之前后须以  ' 括住.
emp.dat 为指定给该程序处理的数据文件文件名.
           
本程序中使用 :  Pattern { Actions } 语法.
Pattern Actions   print $2, $3 * $4  
Pattern 部分被省略, 表无任何限制条件. 故AWK读入每笔资料列
后都将无条件执行这个 Actions.
print为AWK所提供的输出指令, 会将数据输出到stdout(屏幕).
     print 的参数间彼此以 ``{ ,}'' 隔开, 印出数据时彼此间会以空白
     隔开.
(参考 附录 D  内建变量OFS)
将上述的 程序部分 储存于档案  pay1.awk 中. 执行命令时再指定AWK程序文件 之文件名. 这是执行AWK的另一种方式, 特别适用于程
式较大的情况, 其语法如下:
$awk -f AWK程序文件名 数据文件文件名

故执行下列两命令,将产生同样的结果.

$awk -f  pay1.awk  emp.dat
$awk ' { print  $2, $3 * $4 } '  emp.dat
      
读者可使用``-f''参数,让AWK主程序使用其它仅含 AWK函数 的
档案中的函数
其语法如下:
awk  -f AWK主程序文件名 -f AWK函数文件名 数据文件文件名
    (有关 AWK 中函数之宣告与使用于 7.4 中说明)                             
AWK中也提供与 C 语言中类似用法的 printf() 函数. 使用
该函数可进一步控制数据的输出格式.

编辑另一个AWK程序如下, 并取名为  pay2.awk
            
{ printf("\%6s   Work hours: %3d  Pay: %5d\", $2,\$3, $3* $4) }

执行下列命令
         
$awk -f  pay2.awk   emp.dat
               
执行结果屏幕出现:
                Jenny   Work hours: 100 Pay: 21000
                   Dan   Work hours: 110 Pay: 23650
                   Max   Work hours: 130 Pay: 27170
                  John   Work hours: 125 Pay: 27500
                 Linda   Work hours:  95 Pay: 19950


选印合乎指定条件的记录
Pattern { Action }为AWK中最主要的语法. 若某Pattern之值为真则执行它后方的 Action. AWK中常使用``关系判断式'' (Relational Expression)来当成 Pattern.
AWK中除了>;, <, ==, != ,...等关系操作数( Relational Operators )外,另外提供 ~(match),!~(Not Match) 二个关系操作数. 利用这两个操作数, 可判断某字符串是否包含能符合所指定 Regular Expression 的子字符串. 由于这些特性, 很容易使用AWK来撰写需要字符串比对, 判断的程序. [ 范例 :] 承上例,
组装部门员工调薪5%,(组装部门员工之ID.系以``A''开头)
所有员工最后之薪资率若仍低于100, 则以100计.
撰写AWK程序行印新的员工薪资率报表.
[分析 ] : 这个程序须先判断所读入的数据列是否合于指定条件, 再进行某些动作.AWK中 Pattern { Actions } 的语法已涵盖这种 `` if ( 条件 ) { 动作} ''的架构. 编写如下之程序, 并取名 adjust1.awk
$1 ~ /^A.*/ { $3 *= 1.05 } $3<100 { $3 = 100 }

{ printf("%s %8s %d\n", $1, $2, $3)} 执行下列命令 :

$awk -f adjust1.awk emp.dat

结果如下 : 屏幕出现 :
A125 Jenny 105
A341 Dan 115
P158 Max 130
P148 John 125
A123 Linda 100
说 明 :

AWK的工作程序是: 从数据文件中每次读入一笔数据列, 依序执行完程序中所有的 Pattern{ Action }指令 Pattern Actions  
Pattern                                Actions                                       
$1~/^A.*/                                { $3 *= 1.05 }                                       
$3 < 100                                { $3 = 100 }                                       
                                {printf("%s%8s%d\n",$1,$2,$3)}                                       

再从数据文件中读进下一笔记录继续进行处理.
第一个 Pattern { Action }是: $1 ~ /^A.*/ { $3 *= 1.05 } $1 ~ /^A.*/ 是一个Pattern, 用来判断该笔资料列的第一栏是否包含%以``A''开头的子字符串. 其中 /^A.*/ 是一个Regular Expression, 用以表示任何以``A''开头的字符串. (有关 Regular Expression 之用法 参考 附录 E ).
Actions 部分为 $3 *= 1.05 $3 *= 1.05 与 $3 = $3 * 1.05 意义相同. 运算子``*='' 之用法则与 C 语言中一样. 此后与 C 语言中用法相同的运算子或语法将不予赘述.


第二个 Pattern { Actions } 是: $3 <100 {$3 = 100 } 若第三栏的数据内容(表薪资率)小于100, 则调整为100.
第三个 Pattern { Actions } 是: {printf("%s %-8s %d\n",$1, $2, $3 )} 省略了Pattern(无条件执行Actions), 故所有数据列调整后的数据都将被印出.


AWK 中数组的特色

AWK程序中允许使用字符串当做数组的注标(index). 利用
这个特色十分有助于资料统计工作.(使用字符串当注标的数组称为
Associative  Array)   
     首先建立一个数据文件, 并取名为 reg.dat. 此为一学生注册的
资料文件; 第一栏为学生姓名, 其后为该生所修课程.           
              Mary      O.S.         Arch.         Discrete
              Steve     D.S.         Algorithm    Arch.
              Wang    Discrete   Graphics     O.S.
              Lisa       Graphics   A.I.            
              Lily        Discrete   Algorithm   
AWK中数组的特性

使用字符串当数组的注标(index).
使用数组前不须宣告数组名及其大小.
例如 : 希望用数组来记录 reg.dat 中各门课程的修课人数.
          这情况,有二项信息必须储存 :
         (a) 课程名称, 如 : ``O.S.'',``Arch.''.. ,共有哪些课程事前
并不明确.
         (b)各课程的修课人数. 如 : 有几个人修``O.S.''
在AWK中只要用一个数组就可同时记录上列信息. 其方法如下 :
使用一个数组 Number[ ]  :

以课程名称当 Number[ ] 的注标.
以 Number[ ] 中不同注标所对映的元素代表修课人数.

例如 :
有2个学生修 ``O.S.'', 则以 Number[``O.S.''] = 2 表之.
若修``O.S.''的人数增加一人,
则 Number[``O.S.''] = Number[``O.S.''] + 1
或 Number["O.S."]++ .
如何取出数组中储存的信息
以 C 语言为例, 宣告 int  Arr[100]; 之后, 若想得知 Arr[ ]中所
储存的数据, 只须用一个循环, 如 :
for(i=0; i<00; i++) printf("%d\n", Arr);
即可. 上式中 :
数组 Arr[ ] 的注标 : 0, 1, 2,..., 99
数组 Arr[ ] 中各注标所对应的值 : Arr[0], Arr[1],...Arr[99]
但 AWK 中使用数组并不须事先宣告. 以刚才使用的 Number[ ] 而言,
程序执行前, 并不知将来有哪些课程名称可能被当成 Number[ ] 的
注标.
AWK 提供了一个指令, 藉由该指令AWK会自动找寻数组中使用过
的所有注标. 以 Number[ ] 为例, AWK将会找到 ``O.S.'', ``Arch.''",...
使用该指令时, 须指定所要找寻的数组, 及一个变量. AWK会使用
该的变量来记录从数组中找到的每一个注标. 例如
for(course in Number){....}
指定用 course 来记录 AWK 从Number[ ] 中所找到
的注标. AWK每找到一个注标时, 就用course记录该注标之值且
执行{....}中之指令. 藉由这个方式便可取出数组中储存的信息.
(详见下例)

范例 : 统计各科修课人数,并印出结果.
          建立如下程序,并取名为 course.awk:
         {  for( i=2; i
      END{for(coursein Number)
      printf("\%-10s %d\n", course, Number[course] )
         }

  执行下列命令 :
  awk  -f   course.awk    reg.dat
  执行结果如下 :

                  Discrete    3
                  D.S.          1
                  O.S.          2
                  Graphics   2
                  A.I.           1
                  Arch.        2
                  Algorithm  2
                                       
      说  明 :
        
这程序包含二个Pattern { Actions }指令.

Pattern        Actions
        {for( i=2; i< NF; i++) Number[$i]++ }
END        { for( course in Number) printf("\%-10s \%d\n", course, Number[course] )}

第一个Pattern { Actions }指令中省略了Pattern 部分. 故随着
每笔数据列的读入其Actions部分将逐次无条件被执行.
以AWK读入第一笔资料 `` Mary  O.S.  Arch. Discrete" 为例,
因为该笔数据 NF = 4(有4个字段), 故该 Action 的for Loop中
i = 2,3,4.

i        $i                        最初 Number[$i]                                        Number[$i]++ 之后               
2        ``O.S.''                        AWK default Number[``O.S'']=0                                        1               
3        ``Arch.''                        AWK default Number[``Arch'']=0                                        1               
4        ``Discrete''                        AWK default Number[``Discrete'']=0                                        1


第二个 Pattern { Actions }指令中
    * { END}为AWK之保留字, 为{ Pattern}之一种.
    * { END}成立(其值为true)的条件是 :[0.3cm]
     ``AWK处理完所有数据, 即将离开程序时.
      平常读入资料列时,  END并不成立, 故其后的Actions
并不被执行;
      唯有当AWK读完所有数据时, 该Actions才会被执行 ( 注意,
不管数据列有多少笔, END仅在最后才成立, 故该Actions仅被执行
一次.)
     { BEGIN} 与 { END} 有点类似, 是AWK中另一个保留的{Pattern}.
      唯一不同的是 :
``以 { BEGIN 为 Pattern 的 Actions} 于程序一开始执行时, 被执行
一次.''
NF 为AWK的内建变量, 用以表示AWK正处理的数据计列中,
所包含的字段个数.
               
AWK程序中若含有以 $ 开头的自定变量, 都将以如下方式解释 :
以 i= 2 为例, $i = $2 表第二个字段数据. ( 实际上, $  在 AWK 中
为一操作数(Operator), 用以取得字段数据.)

AWK 程序中使用 Shell 命令

AWK程序中允许呼叫Shell指令. 并提供pipe解决AWK与系统间
数据传递的问题. 所以AWK很容易使用系统资源. 读者可利用这个
特色来撰写某些适用的系统工具.
      范例 : 写一AWK程序来打印出线上人数.  
          将下列程序建文件, 命名为 count.awk

       BEGIN  {
                    while ( "who" | getline ) n++
                    print n
                    }
       并执行下列命令 :
       awk { -f} count.awk
       执行结果将会印出目前在线人数
       说 明 :

AWK 程序并不一定要处理资料文件. 以本例而言, 仅输入程序
檔count.awk, 未输入任何数据文件.
BEGIN 和 END 同为AWK中之种一 Pattern. 以 BEGIN 为
Pattern之Actions,只有在AWK开始执行程序,尚未开启任何输入
檔前, 被执行一次.(注意: 只被执行一次 )
``{ |}'' 为 AWK 中表示 pipe的符号. AWK 把 pipe之前的字符串
''who''当成Shell上的命令, 并将该命令送往Shell执行, 执行的结果
(原先应于屏幕印出者)则藉由pipe送进AWK程序中.                  
getline为AWK所提供的输入指令.
          其语法如下 :

语法        由何处读取数据        数据读入后置于
getline var < file        所指定的 file        变量 var(var省略时,表示置于$0)
getline var        pipe        变量 var(var省略时,表示置于$0)
getline var        见 注一        变量 var(var省略时,表示置于$0)

注一 : 当 Pattern 为 BEGIN 或 END 时, getline 将由 stdin 读取数据,
否则由AWK正处理的数据文件上读取数据.
        getline 一次读取一行数据,
                  若读取成功则return 1,
                  若读取失败则return -1,
                  若遇到档案结束(EOF), 则return 0;
             本程序使用 getline 所 return 的数据 来做为 while 判断
循环停止的条件,某些AWK版本较旧,并不容许使用者改变 $0 之值.
这种版的 AWK 执行本程序时会产生 Error, 读者可于 getline 之后
置上一个变量 (如此, getline 读进来的数据便不会被置于 $0 ),
或直接改用gawk便可解决.

AWK 程序的应用实例

本节将示范一个统计上班到达时间及迟到次数的程序.

这程序每日被执行时将读入二个档案 :
            员工当日到班时间的数据文件 ( 如下列之 arr.dat )
            存放员工当月迟到累计次数的档案.
  当程序执行执完毕后将更新第二个档案的数据(迟到次数), 并打印
当日的报表.这程序将分成下列数小节逐步完成, 其大纲如下 :
     
[7.1] 于到班资料文件 {arr.dat} 之前端增加一列抬头
"ID  Number    Arrvial Time", 并产生报表输出到档案today_rpt1 中''
< 在AWK中如何将数据输出到档案  >;
[7.2]将 {today\_rpt1} 上之数据按员工代号排序, 并加注执行当日
              之日期; 产生档案 today_rpt2
             < AWK中如何运用系统资源及AWK中Pipe之特性 >;
[7.3]< 将AWK程序包含在一个shell script档案中>;
[7.4] 于 today_rpt2 每日报表上, 迟到者之前加上"*", 并加注当日
平均到班时间; 产生档案 today_rpt3

[7.5] 从档案中读取当月迟到次数, 并根据当日出勤状况更新迟到累计数.
< 使用者于AWK中如何读取档案数据  >;
某公司其员工到勤时间档如下, 取名为 {arr.dat}. 档案中第一栏为
员工代号, 第二栏为到达时间. 本范例中, 将使用该档案为数据文件.

                1034    7:26
                1025    7:27
                1101    7:32
                1006    7:45
                1012    7:46
                1028    7:49
                1051    7:51
                1029    7:57
                1042    7:59
                1008    8:01
                1052    8:05
                1005    8:12

将数据直接输出到档案

AWK中并未提供如 C 语言中之fopen() 指令, 也未有fprintf()
档案输出之指令. 但AWK中任何输出函数之后皆可藉由使用与
UNIX 中类似的 I/O  Redirection , 将输出的数据 Redirect 到指定
的档案; 其符号仍为 >; (输出到一个新产生的档案) 或 >;>; ( append
输出的数据到档案末端 ).

[例 :]于到班资料文件 arr.dat 之前端增加一列抬头如下 :
"ID  Number    Arrival Time", 并产生报表输出到档案 today_rpt1中      

建立如下档案并取名为reformat1.awk

BEGIN  { print `` ID  Number  Arrival Time''  >; ``today_rpt1''
              print ``==========================='' >; ``today_rpt1''
             }  

             { printf("    %s  %s\n", $1,$2 )  >; "today_rpt1" }      执行:

              $awk  -f reformat1.awk   arr.dat

       执行后将产生档案 today\_rpt1, 其内容如下 :
        ID  Number   Arrival Time
       ============================
           1034         7:26
           1025         7:27
           1101         7:32
           1006         7:45
           1012         7:46
           1028         7:49
           1051         7:51
           1029         7:57
           1042         7:59
           1008         8:01
           1052         8:05
           1005         8:12
      说 明 :

AWK程序中, 文件名称 today_rpt1 之前后须以" 括住, 表示
today_rpt1 为一字符串常数. 若未以"括住, 则 today_rpt1 将
被AWK解释为一个变量名称.
在AWK中任何变量使用之前, 并不须事先宣告. 其初始值为空字符串
(Null string) 或 0.因此程序中若未以 " 将 today_rpt1 括住,
则 today_rpt1 将是一变量, 其值将是空字符串, 这会于执行时
造成错误(Unix 无法帮您开启一个以Null String为档名的档案).

* 因此在编辑 AWK程序时, 须格外留心. 因为若敲错变量名称,
  AWK在编译程序时会认为是一新的变量, 并不会察觉. 如此
  往往会造成 RuntimeError.

BEGIN 为AWK的保留字, 是 Pattern 的一种.
     以 BEGIN 为 Pattern 的 Actions 于AWK程序刚被执行尚未读取
     数据时被执行一次, 此后便不再被执行.
读者或许觉得本程序中的I/O Redirection符号应使用 `` >;>;''
(append)而非 `` >;''.
   \index{ { >;} } \index{ { >;>;} }

本程序中若使用 ``>;'' 将数据重导到 today_rpt1, AWK
第一次执行该指令时会产生一个新档 today_rpt1, 其后再
执行该指令时则把数据append到today_rpt1文件末, 并非每执行
一次就重开一个新檔.

若采用">;>;"其差异仅在第一次执行该指令时, 若已存在
today_rpt1则 AWK 将直接把数据append在原档案之末尾.

这一点, 与UNIX中的用法不同.

AWK 中如何利用系统资源

AWK程序中很容易使用系统资源. 这包括于程序中途叫用 Shell
命令来处理程序中的部分数据; 或于呼叫 Shell 命令后将其产生
之结果交回 AWK 程序(不需将结果暂存于某个档案). 这过程乃是
藉由 AWK 所提供的 pipe (虽然有些类似 Unix 中的 pipe, 但特性
有些不同),及一个从 AWK 中呼叫 Unix 的 Shell command 的语法
来达成.
[例 :] 承上题, 将数据按员工ID排序后再输出到档案 today_rpt2,
并于表头附加执行时的日期.
  分 析 :

AWK 提供与 UNIX 用法近似的 pipe, 其记号亦为 ``|''. 其用法及
涵意如下 :
AWK程序中可接受下列两语法 :
[a. 语法]  AWK output 指令 | ``Shell 接受的命令''
            ( 如 : print $1,$2 | "sort +1n" )  
     
[b. 语法] ``Shell 接受的命令'' |AWK input 指令      
             ( 如 : "ls " | getline)            
      
注 : AWK input 指令只有   getline 一个.
AWK output 指令有   print, printf() 二个.         

于 a 语法中, AWK所输出的数据将转送往 Shell, 由 Shell 的
命令进行处理.以上例而言, print 所印出的数据将经由 Shell 命令
``sort +1n'' 排序后再送往屏幕(stdout).
上列AWK程序中, ``print$1, $2'' 可能反复执行很多次, 其印出的
结果将先暂存于 pipe 中,
等到该程序结束时, 才会一并进行 ``sort +1n''.
须注意二点 : 不论 print \$1, \$2 被执行几次,
       ``sort +1n'' 之 执行时间是  ``AWK程序结束时'',
       ``sort +1n'' 之 执行次数是  ``一次''.
   于 b 语法中, AWK将先叫用 Shell 命令. 其执行结果将经由
pipe 送入AWK程序以上例而言, AWK先令 Shell 执行 ``ls'',
Shell 执行后将结果存于 pipe, AWK指令 getline再从 pipe 中读取
资料.
使用本语法时应留心 : 以上例而言
AWK ``立刻''呼叫 Shell 来执行 ``ls'', 执行次数是一次.
getline 则可能执行多次(若pipe中存在多行数据).      
   除上列 a, b 二语法外, AWK程序中它处若出现像 "date", "cls", "ls"...
        等字符串, AWK只当成一般字符串处理之.


    建立如下档案并取名为 reformat2.awk

    #  程序 reformat2.awk  
    #  这程序用以练习AWK中的pipe
BEGIN {
             "date"  |  getline # Shell 执行 ``date''. getline 取得结果
             并以$0记录
              print " Today is " , $2, $3 >;"today_rpt2"
              print "=========================" >; "today_rpt2"
              print `` ID  Number Arrival Time'' >;``today_rpt2''
              close( "today_rpt2" )
      }

   {printf( "%s  \%s\n", $1 ,$2 )"sort +2n >;>;today_rpt2"}
执行如下命令:
awk -f   reformat2.awk  arr.dat

执行后, 系统会自动将 sort 后的数据加( Append; 因为使用 `` >;>;'')
到档案 today_rpt2末端.  today_rpt2 内容如下 :
         Today is  Sep 17
     =========================
      ID Number   Arrival Time
         1005         8:12
         1006         7:45
         1008         8:01
         1012         7:46
         1025         7:27
         1028         7:49
         1029         7:57
         1034         7:26
         1042         7:59
         1051         7:51
         1052         8:05
         1101         7:32
              
  说 明 :

AWK程序由三个主要部分构成 :
     [ i.]    Pattern { Action} 指令
     [ ii.]   函数主体.  例如 : function double( x ){ return 2*x }
    (参考第11节 Recursive Program )
     [ iii.]  Comment       ( 以 # 开头识别之 )

AWK 的输入指令 getline, 每次读取一列数据. 若getline之后
未接任何变量, 则所读入之资料将以$0 纪录, 否则以所指定的变量
储存之.
[ 以本例而言] :
      执行 "date" | getline 后,
       $0 之值为 "Wed Aug 17 11:04:44 EAT 1994"
  当 $0 之值被更新时, AWK将自动更新相关的内建变量, 如 :
$1,$2,..,NF.故 $2 之值将为"Aug", $3之值将为"17".
  (有少数旧版之AWK不允许即使用者自行更新(update)$0之值,或者
update$0时,它不会自动更新 $1,$2,..NF. 这情况下, 可改用gawk,
或nawk. 否则使用者也可自行以AWK字符串函数split()来分隔$0上
的资料)

  本程序中 printf() 指令会被执行12次( 因为有arr.dat中有12笔
数据), 但读者不用 担心数据被重复sort了12次. 当AWK结束该程序
时才会 close 这个 pipe , 此时才将这12笔数据一次送往系统,
并呼叫 "sort +2n >;>; today_rpt2" 处理之.
AWK提供另一个叫用Shell命令的方法, 即使用AWK函数
            system("shell命令"

        例如 :
              awk '
              BEGIN{
                      system("date  >; date.dat"
                      getline  <date.dat
                      print "Today is ", $2, $3
                    }
                '

        但使用 system( "shell 命令" ) 时, AWK无法直接将执行中的
部分数据输出给Shell 命令. 且 Shell 命令执行的结果也无法直接
输入到AWK中.


执行 AWK 程序的几种方式

本小节中描述如何将AWK程序直接写在 shell script 之中. 此后
使用者执行 AWK 程序时, 就不需要每次都键入
`` awk -f program  datafile''
script 中还可包含其它 Shell 命令, 如此更可增加执行过程的自动化.
建立一个简单的AWK程序 mydump.awk, 如下 :
                  {print}
这个程序执行时会把数据文件的内容 print 到屏幕上( 与cat功用类似 ).
print 之后未接任何参数时, 表示 ``print $0''.
若欲执行该AWK程序, 来印出档案 today_rpt1 及 today_rpt2 的内容时,
必须于 UNIX 的命令列上执行下列命令 :

方式一 awk   -f  mydump.awk  today_rpt1 today_rpt2
方式二 awk ' print '  today_rpt1  today_rpt2第二种方式系将AWK
程序直接写在 Shell 的命令列上, 这种方式仅适合较短的AWK程序.
方式三 建立如下之 shell script, 并取名为 mydisplay,
            awk '     # 注意 , awk 与 ' 之间须有空白隔开
                 {print}                                   
                 ' $*  # 注意 , ' 与 $* 之间须有空白隔开

执行 mydisplay 之前, 须先将它改成可执行的档案(此步骤
往后不再赘述). 请执行如下命令:
$ chmod  +x mydisplay
往后使用者就可直接把 mydisplay 当成指令, 来display任何档案.
例如 :

         $ mydisplay  today_rpt1 today_rpt2

说 明 :

在script档案 mydisplay 中, 指令``awk''与第一个  '
之间须有空格(Shell中并无`` awk' ''指令).
第一个  ' 用以通知 Shell 其后为AWK程序.
第二个  ' 则表示 AWK 程序结束.
故AWK程序中一律以"括住字符串或字符, 而不使用 ' ,
以免Shell混淆.

$* 为 shell script中之用法, 它可用以代表命令列上 ``mydisplay
之后的所有参数''.
例如执行 :
           $ mydisplay today_rpt1 today_rpt2
事实上 Shell 已先把该指令转换成 :
    awk '
             { print}
        '   today_rpt1  today_rpt2
本例中, $* 用以代表 ``today_rpt1 today_rpt2''. 在Shell的语法中,
可用 $1 代表第一个参数, $2 代表第二个参数. 当不确定命令列上的
参数个数时, 可使用 $* 表之.
AWK命令列上可同时指定多个数据文件.  
  以awk  -f dump.awk  today_rpt1 today_rpt2hf 为例
  AWK会先处理today_rpt1, 再处理 today_rpt2. 此时若档案
  无法开启, 将造成错误.
  例如: 未存在档案"file_no_exist", 则执行 :
  awk  -f dump.awk  file_no_exit
  将产生Runtime Error(无法开启档案).
  但某些AWK程序 ``仅'' 包含以 BEGIN 为Pattern的指令. 执行这种
  AWK程序时, AWK并不须开启任何数据文件.此时命令列上若指定
一个不存在的数据文件,并不会产生 ``无法开启档案''的错误.(事实上
AWK并未开启该档案)
例如执行:
awk 'BEGIN {print "Hello,World!!"} ' file_no_exist
该程序中仅包含以 BEGIN 为 Pattern 之 Pattern {actions}, AWK
执行时并不会开启任何数据文件; 故不会因不存在档案file_no_exit而
产生 `` 无法开启档案''的错误.  
AWK会将 Shell 命令列上AWK程序(或 -f 程序文件名)之后的所有
字符串, 视为将输入AWK进行处理的数据文件文件名.
若执行AWK的命令列上 ``未指定任何数据文件文件名'', 则将stdin视为
输入之数据来源, 直到输入end of file( Ctrl-D )为止.
读者可以下列程序自行测试, 执行如下命令 :
       $awk  -f dump.awk   (未接任何资料文件文件名)

       $ mydisplay         (未接任何资料文件文件名)
将会发现 : 此后键入的任何数据将逐行复印一份于屏幕上. 这情况
不是机器当机 ! 是因为AWK程序正处于执行中. 它正按程序指示,
将读取数据并重新dump一次; 只因执行时未指定数据文件文件名, 故AWK
便以stdin(键盘上的输入)为数据来源.
读者可利用这个特点, 设计可与AWK程序interactive talk的程序.

改变 AWK 切割字段的方式 & 使用者定义函数
      
AWK不仅能自动分割字段, 也允许使用者改变其字段切割方式以
适应各种格式之需要. 使用者也可自定函数, 若有需要可将该函数
单独写成一个档案,以供其它AWK程序叫用.

范例 : 承接 6.2 的例子, 若八点为上班时间, 请加注 ``*''于迟到记录
之前, 并计算平均上班时间.

分  析:

  因八点整到达者,不为迟到, 故仅以到达的小时数做判断是不够的;
仍应参考到达时的分钟数. 若 ``将到达时间转换成以分钟为单位'',
不仅易于判断是否迟到, 同时也易于计算到达平均时间.

到达时间($2)的格式为 dd:dd 或 d:dd; 数字当中含有一个 ":".
但文数字交杂的数据AWK无法直接做数学运算. (注: AWK中字符串
"26"与数字26, 并无差异, 可直接做字符串或数学运算, 这是AWK重要
特色之一. 但AWK对文数字交杂的字符串无法正确进行数学运算).

解决之方法 :

方法一.
对到达时间($2) d:dd 或 dd:dd 进行字符串运算,分别取出到达的小时数
及分钟数.
           
首先判断到达小时数为一位或两位字符,再呼叫函数分别截取分钟数
及小时数.

此解法需使用下列AWK字符串函数:

length( 字符串 ) : 传回该字符串之长度.
substr( 字符串,起始位置 ,长度 ) :传回从起始位置起, 指定长度
之子字符串. 若未指定长度, 则传回起始位置到自串末尾之子字符串.
所以:

小时数 = substr( $2, 1,  length($2) - 3 )
分钟数 = substr( $2, length($2) - 2 )

[方法二]  
                 改变输入列字段的切割方式, 使AWK切割字段后分别将
                 小时数及分钟数隔开于二个不同的字段.
                 字段分隔字符 FS (field seperator) 是AWK的内建变数,
                 其默认值是空白及tab. AWK每次切割字段时都会先参考
                  FS 的内容. 若把":"也当成分隔字符, 则AWK 便能自动把
                 小时数及分钟数分隔成不同的字段.
                 故令
   FS = "[ \t:]+" (注 : [ \t:]+ 为一Regular Expression )

Regular Expression 中使用中括号 [ ...  ] 表一字符集合,
用以表示任意一个位于两中括号间的字符.
故可用``[ \t:]''表示 一个 空白 , tab 或 ``:''
Regular Expression中使用 ``+'' 形容其前方的字符可出现一次
或一次以上.
故 ``[ \t:]+'' 表示由一个或多个 ``空白, tab 或 : '' 所组成的字符串.

设定 FS =''[ \t:]+'' 后, 资料列如 : ``1034    7:26'' 将被分割成3个字段

第一栏        第二栏        第三栏
$1         $2         $3
1034        7        26
getline var        见 注一        变量 var(var省略时,表示置于$0)

明显地, AWK程序中使用方法一比方法二更简洁方便. 本范例中采用
方法二,也藉此示范改变字段切割方式之用途.

编写AWK程序 reformat3, 如下 :
awk '
BEGIN  {
            {FS= "[ \t:]+"  #改变字段切割的方式            
            "date"  | getline  # Shell 执行 ``date''. getline 取得结果以$0纪录
             print " Today is " ,$2, $3  >; "today_rpt3"
             print "=========================">;"today_rpt3"
             print `` ID  Number Arrival Time'' >; ``today_rpt3''
             close( "today_rpt3" )
            }

    {
   #已更改字段切割方式, $2表到达小时数, $3表分钟数
arrival = HM_to_M($2, $3)
printf("  %s   %s:%s %s\n", $1,$2, $3
, arrival  >; 480 ? "*": " "  ) | "sort +0n>;>;today_rpt3"
total += arrival
END {
            close("today_rpt3"   #参考本节说明 5
            close("sort +0n  >;>; today_rpt3"
           printf(" Average arrival time : %d:%d\n",
                  total/NR/60, (total/NR)%60 ) >;>; "today_rpt3"
         }
      function HM_to_M( hour, min ){
              return  hour*60 + min
             }
      ' $*
并执行如下指令 :
$ reformat3  arr.doc
   执行后,档案 today_rpt3 的内容如下:
        Today is  Sep 21
     =========================
      ID  Number Arrival Time
          1005      8:12  *
          1006      7:45   
          1008      8:01  *
          1012      7:46   
          1025      7:27   
          1028      7:49   
          1029      7:57   
          1034      7:26   
          1042      7:59   
          1051      7:51   
          1052      8:05  *
          1101      7:32   
      Average arrival time : 7:49
{verbatim}  
  说 明 :

AWK 中亦允许使用者自定函数. 函数定义方式请参考本程序,
function 为 AWK 的保留字.
HM_to_M( ) 这函数负责将所传入之小时及分钟数转换成
以分钟为单位.  使用者自定函数时, 还有许多细节须留心, 如
data scope,..
( 请参考 第十节 Recursive Program)

AWK中亦提供与 C 语言中相同的 Conditional Operator. 上式
printf()中使用arrival >;480 ? "*" : " "}即为一例
若 arrival 大于 480 则return "*" , 否则return " ".
% 为AWK之运算子(operator), 其作用与 C 语言中之 % 相同
(取余数).
NR(Number of  Record) 为AWK的内建变数. 表AWK执行该程序
后所读入的纪录笔数.
  AWK 中提供的 close( )指令, 语法如下(有二种) :

close( filename )
close( 置于pipe之前的command )
为何本程序使用了两个 close( ) 指令 :

指令 close( "sort +2n >;>; today_rpt3" ), 其意思为 close 程序中
置于 "sort +2n >;>; today_rpt3 " 之前的 Pipe, 并立刻呼叫 Shell 来
执行"sort +2n  >;>; today_rpt3".
(若未执行这指令, AWK必须于结束该程序时才会进行上述动作;
则这12笔sort后的数据将被 append 到档案 today_rpt3 中
"Average arrival time : ..." 的后方)
因为 Shell 排序后的数据也要写到 today_rpt3, 所以AWK必须
先关闭使用中的today_rpt3 以利 Shell 正确将排序后的数据
append 到today_rpt3否则2个不同的 process 同时开启一
档案进行输出将会产生不可预期的结果.

读者应留心上述两点,才可正确控制数据输出到档案中的顺序.

指令 close("sort +0n >;>; today_rpt3"中字符串 "sort +0n >;>; today_rpt3"
须与 pipe | 后方的 Shell Command 名称一字不差, 否则AWK将视为
二个不同的 pipe.
读者可于BEGIN{}中先令变数 Sys_call = "sort +0n  >;>; today_rpt3",
程序中再一律以 Sys_call 代替该字符串.

使用 getline 来读取数据

范 例 : 承上题,从档案中读取当月迟到次数, 并根据当日出勤状况
更新迟到累计数.(按不同的月份累计于不同的档案)

分 析:

   程序中自动抓取系统日期的月份名称, 连接上``late.dat'',
形成累计迟到次数的文件名称(如 : Jullate.dat,...), 并以变数
late_file纪录该文件名.

累计迟到次数的档案中的数据格式为 :
                         员工代号(ID) 迟到次数
      例如, 执行本程序前档案 Auglate.dat 的内容为 :

                 1012 0
                 1006 1
                 1052 2
                 1034 0
                 1005 0
                 1029 2
                 1042 0
                 1051 0
                 1008 0
                 1101 0
                 1025 1
                 1028 0   



编写程序 reformat4.awk 如下:
   awk '
BEGIN {
           Sys_Sort = "sort +0n >;>; today_rpt4"
           Result = "today_rpt4"
           # 改变字段切割的方式
          # 令 Shell执行``date''; getline 读取结果,并以$0纪录               
           FS = "[\t:]+"  
          "date" |  getline      
          print " Today is " , $2, $3 >;Result
          print "=========================" >; Result
          print `` ID  Number Arrival Time'' >; Result
          close( Result )
# 从文件按中读取迟到数据, 并用数组cnt[ ]记录. 数组cnt[ ]中以
员工代号为# 注标, 所对应的值为该员工之迟到次数.
late_file = $2 "late.dat"            
while( getline  < late_file >;0 )  cnt[$1] = $2
close( late_file )
}
{  
  # 已更改字段切割方式, $2表小时数,$3表分钟数
  arrival = HM_to_M($2, $3)  
  if( arrival >; 480 ){
  mark = "*"  # 若当天迟到,应再增加其迟到次数, 且令
  mark 为''*''.cnt[$1]++ }   
  else   mark = " "
           
# message 用以显示该员工的迟到累计数, 若未曾迟到
message 为空字符串
message = cnt[$1] ? cnt[$1] " times" : ""
   printf("%s%2d:%2d %5s %s\n", $1, $2, $3, mark,
message ) | Sys_Sort
         total += arrival
    }
END  {
           close( Result )
           close( Sys_Sort )
           printf(" Average arrival time : %d:%d\n", total/NR/60,
       (total/NR)%60 ) >;>; Result
           #将数组cnt[ ]中新的迟到数据写回档案中
           for( any in cnt )
                 print any, cnt[any] >; late_file
          }
   function HM_to_M( hour, min ){
                 return hour*60 + min
           }
  ' $*
执行后, today_rpt4 之内容如下 :

          Today is  Aug 17
    ================================
      ID Number  Arrival Time
         1005        8:12       * 1 times
         1006        7:45         1 times
         1008        8: 1       * 1 times
         1012        7:46         
         1025        7:27         1 times
         1028        7:49         
         1029        7:57         2 times
         1034        7:26         
         1042        7:59         
         1051        7:51         
         1052        8: 5       * 3 times
         1101        7:32         
     Average arrival time : 7:49

*
说  明 :

latefile 是一变量, 用以记录迟到次数的档案之档名.
latefile之值由两部分构成, 前半部是当月月份名称(由呼叫
"date"取得)后半部固定为"late.dat" 如 : Junlate.dat.

指令 getline <latefile 表由latefile所代表的档案中
读取一笔纪录, 并存放于$0.
若使用者可自行把数据放入$0, AWK会自动对这新置入 $0 的数据
进行字段分割. 之后程序中可用$1, $2,..来表示该笔资料的第一栏,
第二栏,..,
              
(注: 有少数AWK版本不容许使用者自行将数据置于 $0, 遇此情况可改
用gawk或nawk)

执行getline指令时, 若成功读取纪录,它会传回1. 若遇到档案结束,
它传回0; 无法开启档案则传回-1.

利用 while( getline  < filename >;0 ) {....}可读入档案中
的每一笔数据并予处理. 这是AWK中user自行读取档案数据的
一个重要模式.

数组 late_cnt[ ] 以员工ID. 当注标(index), 其对应值表其迟到的
次数.

执行结束后, 利用 for(Variable in array ){..}之语法
for( any in late_cnt ) print any, late_cnt[any]>; latefile
将更新过的迟到数据重新写回记录迟到次数之档案. 该语法于第5节
中曾有说明.

处理 Multi-line 记录
AWK每次从数据文件中只读取一笔Record, 进行处理. AWK系依照其内建变量 RS(Record Separator) 的定义将档案中的数据分隔成一笔一笔的Record. RS 的默认值是 "\n"(跳行符号), 故平常AWK中一行数据就是一笔 Record. 但有些档案中一笔Record涵盖了数行数据, 这种情况下不能再以 "\n" 来分隔Records. 最常使用的方法是相邻的Records之间改以 一个空白行 来隔开. 在AWK程序中, 令 RS = ""(空字符串)后, AWK把会空白行当成来档案中Record的分隔符. 显然AWK对 RS = "" 另有解释方式,简略描述如下, 当 RS = "" 时 :
数个并邻的空白行, AWK仅视成一个单一的Record Saparator. (AWK不会于两个紧并的空白行之间读取一笔空的Record)
AWK会略过(skip)檔首或檔末的空白行. 故不会因为档首或档末的空白行,造成AWK多读入了二笔空的数据.
请观察下例,首先建立一个数据文件 week.rpt如下: 张长弓 GNUPLOT 入门 吴国强 Latex 简介 VAST-2 使用手册 mathematica 入门 李小华 AWK Tutorial Guide Regular Expression 该档案档首有数列空白行, 各笔Record之间使用一个或数个空白行隔开. 读者请细心观察, 当 RS = "" 时, AWK读取该数据文件之方式. 编辑一个AWK程序档案 make_report如下: awk ' BEGIN { FS = "\n" RS = "" split( "一. 二. 三. 四. 五. 六. 七. 八. 九.", C\_Number, " " ) } { printf("\n%s 报告人 : %s \n",C_Number[NR],$1) for( i=2; i { >;}= NF; i++) printf(" %d. %s\n", i-1, $i) } ' $ 执行 $ make_report week.rpt 屏幕产生结果如下: 一. 报告人 : 张长弓 1. GNUPLOT 入门 二. 报告人 : 吴国强 1. Latex 简介 2. VAST-2 使用手册 3. mathematica 入门 三. 报告人 : 李小华 1. AWK Tutorial Guide 2. Regular Expression 说明:
本程序同时也改变字段分隔字符( FS= "\n", 如此一笔数据中的每一行都是一个field. 例如 : AWK读入的第一笔 Record 为张长弓 GNUPLOT 入门 其中 $1 指的是"张长弓", $2 指的是"GNUPLOT 入门"
上式中的C\_Number[ ]是一个数组(array), 用以记录中文数字. 例如 : C\_Number[1] = "一", C\_Number[2] = "二" 这过程使用AWK字符串函数 split( ) 来把中文数字放进数组 Number[ ]中. 函数 split( )用法如下 : split( 原字符串, 数组名, 分隔字符(field separator) ) : AWK将依所指定的分隔字符(field separator)分隔原字符串成一个个的字段(field), 并以指定的 数组 记录各个被分隔的字段

如何读取命令列上的参数

大部分的应用程序都容许使用者于命令之后增加一些选择性的参数.
执行AWK时这些参数大部分用于指定数据文件文件名, 有时希望在程序
中能从命令列上得到一些其它用途的数据. 本小节中将叙述如何在
AWK程序中取用这些参数.
    建立档案如下, 命名为 see_arg :
{
        awk '
        BEGIN {
   for( i=0; i<ARGC ; i++)
        print ARGV  # 依次印出AWK所纪录的参数
}
'$*
执行如下命令 :
$  see_arg  first-arg  second-arg
结果屏幕出现 :
awk
     first-arg
     second-arg

  说明 :

ARGC, ARGV[ ] 为AWK所提供的内建变量.
   
   ARGC : 为一整数. 代表命令列上, 除了选项-v, -f 及其对应
   的参数之外所有参数的数目.
   ARGV[ ] : 为一字符串数组. ARGV[0],ARGV[1],...ARGV[ARGC-1].
   分别代表命令列上相对应的参数.
   
   例如, 当命令列为 :
   $awk  -vx=36  -f program1  data1  data2
    或
    awk '{ print $1 ,$2 }'  data1  data2
            其  ARGC    之值为  3
            ARGV[0] 之值为  "awk"
            ARGV[1] 之值为  "data1"
            ARGV[2] 之值为  "data2"

命令列上的 "-f program1", " -vx=36", 或程序部分 '{ print $1, $2}'
都不会列入 ARGC 及 ARGV[ ] 中.

AWK 利用 ARGC 来判断应开启的数据文件个数.
但使用者可强行改变 ARGC; 当 ARGC 之值被使用者设为 1 时;
AWK将被蒙骗,误以为命令列上并无数据文件文件名, 故不会以 ARGV[1],
ARGV[2],..为文件名来开文件读取数据; 但于程序中仍可藉由 ARGV[1],
ARGV[2],..来取得命令列上的资料.

某一程序  test1.awk  如下 :
BEGIN{
            number = ARGC   #先用number 记住实际的参数个数.
            ARGC = 2 # 自行更改 ARGC=2, AWK将以为只有一个
                               资料文件
                            # 仍可藉由ARGV[ ]取得命令列上的资料.
             for( i=2; i< number; i++) data = ARGV
          }

     ........

       于命令列上键入
$awk  -f test1.awk  data_file  apple  orange
执行时 AWK 会开启数据文件 data_file 以进行处理. 不会开启以
apple,orange 为档名的档案(因为 ARGC 被改成2). 但仍可藉由
ARGV[2], ARGV[3]取得命令列上的参数 apple, orange
   
  可以下列命令来达成上例的效果.

$awk -f test2.awk -v data[2]="apple" -v data[3]="orange" data_file


撰写可与使用者相互交谈的 AWK 程序

执行AWK程序时, AWK会自动由档案中读取数据来进行
处理, 直到档案结束.只要将AWK读取数据的来源改成键盘输入,
便可设计与AWK interactive talk 的程序.
本节将提供一个该类程序的范例.
[范例 :]  本节将撰写一个英语生字测验的程序, 它将印出中文字意,
再由使用者回答其英语生字.
首先编辑一个数据挡 test.dat (内容不拘,格式如下)

         apple   苹果
         orange  柳橙
         banana  香蕉
         pear    梨子
         starfruit 杨桃
         bellfruit 莲雾
         kiwi     奇异果
         pineapple 菠萝
         watermelon 西瓜


编辑AWK程序"c2e"如下:

awk '
BEGIN {
           while( getline < ARGV[1] ){ #由指定的档案中读取测验数据
           English[++n] = $1 # 最后, n 将表示题目之题数
           Chinese[n] = $2  
            }           
           ARGV[1] = "-"  # "-"表示由stdin(键盘输入)
           srand()           # 以系统时间为随机数启始的种子
           question( )      #产生考题
                        
            }
  

    {# AWK自动读入由键盘上输入的数据(使用者回答的答案)
        if($1 != English[ind] )
             print "Try again!"
        else{
        print "\nYou are right !! Press Enter to Continue --- "
        getline
        question( )#产生考题
    }  
  }

function question(){
        ind = int(rand( )* n) + 1 #以随机数选取考题
        system("clear"
        print " Press\"ctrl-d\" to exit"
        printf("\n%s ", Chinese[ind] " 的英文生字是: "
  }
  '$*

执行时键入如下指令 :
   $c2e  test.dat
  屏幕将产生如下的画面:
  Press "ctrl-d " to exit
       莲雾 的英文生字是:
  若输入 bellfruit
程序将产生
    You are right !! Press Enter to Continue ---
}
  说 明 :


参数 test.dat (ARGV[1]) 表示储存考题的数据文件文件名.
AWK 由该档案上取得考题资料后, 将 ARGV[1] 改成 "-".
"-" 表示由 stdin(键盘输入) 数据. 键盘输入数据的结束符号 (End of file)
是 Ctrl-d. 当 AWK 读到 Ctrl-d 时就停止由 stdin 读取数据.
AWK的数学函数中提供两个与随机数有关的函数.
rand( ) : 传回介于 0与1之间的(近似)随机数值. 0

使用 AWK 撰写 Recusive Program

AWK 中除了函数的参数列(Argument List)上的参数(Arguments)外,
所有变量不管于何处出现全被视为 Global variable. 其生命持续
至程序结束 --- 该变量不论在function外或 function内皆可使用,
只要变量名称相同所使用的就是同一个变量,直到程序结束.
因 Recusive 函数内部的变量, 会因它呼叫子函数(本身)而重复使用,
      故撰写该类函数时, 应特别留心.
例如 : 执行
awk '
BEGIN
{
x = 35
y = 45
test_variable( x )
printf("Return to  main : arg1= %d, x= %d, y= %d, z= %d\n",
arg1, x, y, z)
}

  function test_variable( arg1 )
{
arg1++  # arg1 为参数列上的参数, 是local variable. 离开此函数
后将消失.
y ++       # 会改变主式中的变量 y
z = 55     # z 为该函数中新使用的变量, 主程序中变量 z 仍可被使用.
printf("Inside the function: arg1=%d,x=%d, y=%d, z=%d\n",
     arg1, x, y, z)
} '
结果屏幕印出

         Inside the function: arg1= 36,x= 35, y= 46, z= 55
         Return to main     : arg1= 0, x= 35, y= 46, z= 55

  由上可知 :

函数内可任意使用主程序中的任何变量.
函数内所启用的任何变量(除参数外), 于该函数之外依然可以使用.

此特性优劣参半, 最大的坏处是式中的变量不易被保护, 特别是
recursive呼叫本身, 执行子函数时会破坏父函数内的变量.
权变的方法是 : 在函数的 Argument list 上虚列一些 Arguments.
函数执行中使用这些虚列的 Arguments 来记录不想被破坏的数据,
如此执行子函数时就不会破坏到这些数据. 此外AWK 并不会检查,
呼叫函数时所传递的参数个数是否一致.
例如 : 定义 recursive function 如下 :
          function  demo( arg1 )# 最常见的错误例子
              ........
               for(i=1; i< 20 ; i++){
                 demo(x)
  # 又呼叫本身. 因为 i 是 global variable, 故执行完该子函数后
  # 原函数中的 i 已经被坏, 故本函数无法正确执行.
.......
        }
..........     
  }
可将上列函数中的 i 虚列在该函数的参数列上, 如此 i 便是一个
local variable, 不会因执行子函数而被破坏.
将上列函数修改如下:
function  demo( arg1,     i )
{
......
for(i=1; i< 20; i++)
     {
      demo(x)#AWK不会检查呼叫函数时, 所传递的参数个数是否一致
      .....
      }
}   
$0, $1,.., NF, NR,..也都是 global variable, 读者于 recusive function
中若有使用这些内建变量, 也应另外设立一些 local variable 来保存,
以免被破坏.
范例 :以下是一个常见的 Recursive 范例. 它要求使用者输入一串元素
(各元素间用空白隔开) 然后印出这些元素所有可能的排列.
编辑如下的AWK式, 取名为 permu
awk '
BEGIN
{
print "请输入排列的元素,各元素间请用空白隔开"
getline
permutation($0, ""  
printf("\n共 %d 种排列方式\n", counter)
}
function  permutation( main_lst, buffer, new_main_lst, nf, i, j )
{
  $0 = main_lst  # 把main_lst指定给$0之后AWK将自动进行
  字段分割.
nf = NF            # 故可用 NF 表示 main_lst 上存在的元素个数.
#  BASE CASE : 当main_lst只有一个元素时.
     if( nf == 1)
    {     
     print buffer main_lst # buffer的内容连接(concate)上 main_lst 就
     counter++             # 是完成一次排列的结果
     return
    }
    # General Case : 每次从 main\_lst 中取出一个元素放到buffer中
    #  再用 main_lst 中剩下的元素 (new_main_lst) 往下进行排列
      else for( i=1; i< =nf ;i++)
      {
       $0 = main_lst # $0($1,$2,..$j,,)为Global variable已被坏, 故重新
                           #  把 main\_lst 指定给\$0, 令AWK再做一次字段分割  
                 new_main_lst = ""
                 for(j=1; j< =nf; j++) # concate new_main_lst  
                    if( j != i )  new_main_lst = new_main_lst " " $j
                 permutation( new_main_lst, buffer " " $i  )
      }
  }
'$*
执行     $ permu
  屏幕上出现

              请输入排列的元素,各元素间请用空白隔开
  若输入 1 2 3 结果印出

              1 2 3
              1 3 2
              2 1 3
              2 3 1
              3 1 2
              3 2 1

             共 6 种排列方式


说 明 :

有些较旧版的AWK,并不容许使用者指定$0之值. 此时可改用
gawk, 或 nawk.
否则也可自行使用 split() 函数来分割 main_lst.

为避免执行子函数时破坏 new_main_lst, nf, i, j 故把这些变数
也列于参数列上. 如此, new_main_lst, nf, i, j 将被当成 local variable,
而不会受到子函数中同名的变量影响. 读者宣告函数时,参数列上
不妨将这些 ``虚列的参数'' 与真正用于传递信息的参数间以较长
的空白隔开, 以便于区别.
AWK 中欲将字符串concatenation(连接)时, 直接将两字符串并置
即可(Implicit Operator).
       例如 :

    awk '
   BEGIN{
          A = "This "
          B = "is a "
          C = A B "key." # 变量A与B之间应留空白,
                                         否则''AB''将代表另一新变量.
            print C
                   }
           }
        结果将印出
        This is a key.

  AWK使用者所撰写的函数可再reuse, 并不需要每个AWK式中
都重新撰写.
将函数部分单读编写于一档案中, 当需要用到该函数时再以下列方式
include进来.
$ awk  -f 函数裆名 -f AWK主程序文件名  数据文件文件名  


Appendix A Patterns

AWK 藉由判断 Pattern 之值来决定是否执行其后所对应的
Actions.这里列出几种常见的 Pattern :

BEGIN  
BEGIN 为 AWK 的保留字, 是一种特殊的 Pattern.
BEGIN 成立(其值为true)的时机是 :
           ``AWK 程序一开始执行, 尚未读取任何数据之前.''
所以在 BEGIN { Actions } 语法中, 其 Actions 部份仅于程序
一开始执行时被执行一次. 当 AWK 从数据文件读入数据列后,
BEGIN 便不再成立, 故不论有多少数据列, 该 Actions 部份仅被执行
一次.
一般常把 ``与数据文件内容无关'' 与 ``只需执行ㄧ次'' 的部分置于该
Actions(以 BEGIN 为 Pattern)中.
例如 :
BEGIN {
        FS = "[ \t:]"   # 于程序一开始时, 改变AWK切割字段的方式
        RS = ""         # 于程序一开始时, 改变AWK分隔数据列的方式
        count = 100     # 设定变量 count 的起始值
        print " This is a title line "  # 印出一行 title
      }
       ....... # 其它 Pattern { Actions } .....

有些AWK程序甚至''不需要读入任何数据列''. 遇到这情况可把整个
程序置于以 BEGIN 为 Pattern的 Actions 中.
例如 :
                  BEGIN {   print " Hello ! the Word ! " }

注意 :执行该类仅含 BEGIN { Actions } 的程序时, AWK 并不会开启
任何数据文件进行处理.

END
END 为 AWK 的保留字, 是另一种特殊的 Pattern.
END 成立(其值为true)的时机与 BEGIN 恰好相反, 为 :
``AWK 处理完所有数据, 即将离开程序时''
平常读入资料列时, END并不成立, 故其对应的 Actions 并不被执行;
唯有当AWK读完所有数据时, 该 Actions 才会被执行
注意 : 不管数据列有多少笔, 该 Actions 仅被执行一次.

Relational Expression
使用像 `` A  Relation Operator B'' 的 Expression 当成 Pattern.  
当 A 与 B 存在所指定的关系(Relation)时, 该 Pattern 就算成立(true).
        例如 :
           length($0)< = 80  { print }
        上式中 { length($0)<= 80 是一个 Pattern, 当 $0(数据列)之长度
        小于等于 80 时该 Pattern 之值为true, 将执行其后的 Action
       (印出该资料列).   
        AWK 中提供下列 关系操作数(Relation Operator)


操作数        涵意
>;        大于
<        小于
>; =        大于或等于
<=        小于或等于
==        等于
!=        不等于
~        match
!~        not match

上列关系操作数除~(match)与!~(not match)外与 C 语言中之
涵意一致.
~(match) 与!~(match) 在 AWK 之涵意简述如下 :
若 A 表一字符串, B 表一 Regular Expression.
A ~B  判断 字符串A 中是否 包含   能合于(match)B式样的子字符串.
A !~B 判断 字符串A 中是否 未包含 能合于(match)B式样的子字符串.
     例如 :
$0 ~ /program[0-9]+\.c/  \{ print }
$0 ~/program[0-9]+\.c/ }整个是一个 Pattern, 用来判断$0(资料列)中
是否含有可 match /program[0-9]+\.c/ 的子字符串, 若$0 中含有该类
字符串, 则执行 print (印出该列数据).
Pattern 中被用来比对的字符串为$0 时(如本例), 可仅以 Regular Expression
部分表之.
      故本例的 Pattern 部分
$0 ~/program[0-9]+\.c/ 可仅用/program[0-9]+\.c/表之     

     (有关 match 及 Regular Expression 请参考 附录 E )
Regular Expression
直接使用 Regular Expression 当成 Pattern; 此为 $0 ~
Regular Expression 的简写.
该 Pattern 用以判断 $0(资料列) 中是否含有 match 该 Regular Expression
的子字符串; 若含有该成立(true) 则执行其对应的 Actions.
     例如 :  
/^[0-9]*$/  print "This line is a integer !"
与{ $0 ~/^[0-9]*$/ { print "This line is a integer !" } 相同
Compound Pattern
  之前所介绍的各种 Patterns, 其计算(evaluation)后结果为一逻辑值
(True or False).AWK 中逻辑值彼此间可藉由&&(and),||(or), !(not)
结合成一个新的逻辑值.故不同 Patterns 彼此可藉由上述结合符号
来结合成一个新的 Pattern. 如此可进行复杂的条件判断.
例 如 :
FNR >; = 23 &&   FNR <=28  print "    " $0      }
     上式利用&& (and) 将两个 Pattern 求值的结果合并成一个逻辑值.
      该式 将资料文件中 第23行 到 28行 向右移5格(先印出5个空白
      字符)后印出.
      ( FNR 为AWK的内建变量, 请参考 附录 D )

Pattern1 , Pattern2
遇到这种 Pattern, AWK 会帮您设立一个 switch(或flag).
当AWK读入的资料列使得 Pattern1 成立时, AWK 会打开(turn on)
这 switch.
当AWK读入的资料列使得 Pattern2 成立时, AWK 会关上(turn off)
这个 switch.
该 Pattern 成立的条件是 :
当这个 switch 被打开(turn on)时 (包括 Pattern1, 或 Pattern2 成立
的情况)例 如 :
FNR>;= 23 && FNR< =28  { print "     " $0  }
     可改写为
     FNR == 23 ,  FNR == 28           { print "      " $0 }
说 明 :

当 FNR >;= 23 时, AWK 就 turn on 这个 switch;
因为随着资料列的读入, AWK不停的累加 FNR.
当 FNR = 28 时, Pattern2  FNR == 28 便成立, 这时 AWK
会关上这个 switch.
当 switch 打开的期间, AWK 会执行 ``print "     " $0''

( FNR 为AWK的内建变量, 请参考 附录 D )


Appendix B Actions

Actions 是由下列指令(statement)所组成 :

  expression ( function calls, assignments..)
  print expression-list
  printf( format, expression-list)
  if( expression ) statement [else statement]
  while( expression ) statement
  do statement while( expression)
  for( expression; expression; expression) statement
  for( variable in array) statement
  delete
  break
  continue
  next
  exit [expression]
  statement

AWK 中大部分指令与 C 语言中的用法一致, 此处仅介绍较为常用
或容易混淆之指令的用法.

流程控制指令

if 指令
     语法
           if (expression) statement1 [else statement2 ]
       范例 :

  if( $1>; 25 )
       print "The 1st field is larger than 25"
  else print "The 1st field is not larger than 25"

(a)与 C 语言中相同, 若 expression 计算(evaluate)后之值不为 0
或空字符串, 则执行 statement1; 否则执行 statement2.
(b)进行逻辑判断的expression所传回的值有两种, 若最后的逻辑值
为true, 则传回1, 否则传回0.
(c)语法中else statement2 以[ ] 前后括住表示该部分可视需要而
予加入或省略.

while 指令
     语法 :
             while( expression ) statement
     范例 :

             while( match(bu
 楼主| 发表于 2005-11-1 14:33:18 | 显示全部楼层
太大。。。好像残缺 :-(
回复

使用道具 举报

发表于 2005-11-1 18:36:56 | 显示全部楼层
真的好大,不过挺好。温故了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

GMT+8, 2025-2-12 17:03 , Processed in 0.051018 second(s), 16 queries .

© 2001-2025 Discuz! Team. Powered by Discuz! X3.5.

快速回复 返回顶部 返回列表