shell基础67:执行顺序(||及&&,{}及())及正则表表达式
引用:
$ unset A
$ [ -n "$A" ] && [ "$A" -lt 100 ] || echo 'too big!'
too big!
为何上面的结果也可得到呢? 做个记号
网中人第十问
10) && 与 || 差在哪?
第六问:
6) exec 跟 source 差在哪?
总结:
fork:在子行程中的环境如何变更,均不会影响父行程的环境。
正常来说,当我们执行一个 shell script 时,其实是先产生一个 sub-shell 的子行程,然后 sub-shell 再去产生命令行的子行程。
即我们正常运行一个脚本时:
代码:
./my.script
source:所谓 source 就是让 script 在当前 shell 内执行、而不是产生一个 sub-shell 来执行。
由于所有执行结果均于当前 shell 内完成,若 script 的环境有所改变,当然也会改变当前环境了
代码:
source ./my.script
或:
. ./my.script
()和{ }
引用:
如果希望把几个命令合在一起执行, s h e l l提供了两种方法。既可以在当前s h e l l也可以在子s h e l l中执行一组命令。
1、():
为了在当前s h e l l中执行一组命令,可以用命令分隔符隔开每一个命令,并把所有的命令用圆括号()括起来。
它的一般形式为:
代码:
(命令1;命令2;. . .)
2、{}:
使用{ }来代替(),那么相应的命令将在子s h e l l中作为一个整体被执行,只有在{ }中所有命令的输出作为一个整体被重定向时,其中的命令才被放到子s h e l l中执行,否则在当前s h e l l执行。它的一般形式为:
代码:
{命令1;命令2;. . . }
此段有误,现更下如下:
代码:
(cmd1;cmd2;...;cmdN)#在一个子shell里执行一组命令
{cmd1;cmd2;...;cmdN}# 在当前shell里执行一组命令
这是一个基本概念
[jason@firewall jason]$ A=1;echo $A;{ A=2; };echo $A
1
2
[jason@firewall jason]$ A=1;echo $A;( A=2; );echo $A
1
1
{ A=2; }改变了当前shell变量的值
( A=2; )未改变当前shell变量的值
可查看原链接
多谢指出,呵呵
例一:上面的例子中:
代码:
$ comet month_end.txt || exit
现在如果该脚本执行失败了,我希望先给自己发个邮件,然后再退出,可以用下面的方法来实现:
代码:
$ comet month_end || (echo "Comet did no work" | mail sam ; exit)
上例中由于 只使用了命令分隔符而没有把它们组合在一起, s h e l l将直接执行最后一个命令(e x i t)。
例二:下面是原来的那个例子:
代码:
$ sort quarter_end.txt > quarter.sorted && lp quarter.sorted
使用命令组合的方法,如果s o r t命令执行成功了,先将输出文件拷贝到一个日志区,然后再打印。
代码:
$ sort quarter_end.txt > quarter.sorted && (cp quarter.sorted /logs/quarter.sorted; lp quarter.sorted)
shell十三问第七问:
7) ( ) 与 { } 差在哪?
代码:
& #8226; 匹配行首与行尾。
? 匹配数据集。
? 只匹配字母和数字。
? 匹配一定范围内的字符串集。
当从一个文件或命令输出中抽取或过滤文本时,可以使用正则表达式( R E),正则表达式是一些特殊或不很特殊的字符串模式的集合。
基本元字符集及其含义
网中人 写到:
abc: 表示 abc 三個連續的字符, 但彼此獨立而非集合. (可簡單視為三個 char. set)
(abc): 表示 abc 這三個連續字符的集合. (可簡單視為一個 char. set)
a|b: 表示單一字符, 或 a 或 b .
(abc|xyz): 表示或 abc 或 xyz 這兩個 char. set 之一. (註二)
[abc]: 表示單一字符, 可為 a 或 b 或 c . (與 wildcard 之 [abc] 原理相同)
[^abc]: 表示單一字符, 不為 a 或 b 或 c 即可. (與 wildcard 之 [!abc] 原理相同)
. : 表示任意單一字符. (與 wildcard 之 ? 原理相同)
代码:
^ 只只匹配行首
$ 只只匹配行尾
* 只一个单字符后紧跟*,匹配0个或多个此单字符
[ ] 只匹配[ ]内字符。可以是一个单字符,也可以是字符序列。可以使用-
表示[ ]内字符序列范围,如用[ 1 - 5 ]代替[ 1 2 3 4 5 ]
只用来屏蔽一个元字符的特殊含义。因为有时在s h e l l中一些元字符有
特殊含义。可以使其失去应有意义
. 只匹配任意单字符
p a t t e r n { n } 只用来匹配前面p a t t e r n出现次数。n为次数
p a t t e r n { n, } m 只含义同上,但次数最少为n
p a t t e r n { n,m } 只含义同上,但p a t t e r n出现次数在n与m之间
现在详细讲解其中特殊含义。
1、使用句点匹配单字符
例一:beng.n:以b e g开头,中间夹一个任意字符。
例二:. . . .X C. . . .:共10个字符,前四个之后为XC
例三:列出所有用户都有写权限的目录或文件 :
代码:
ls -l |grep ...x..x..x
2、行首以^匹配字符串或字符序列
^只允许在一行的开始匹配字符或单词。
例如,使用ls -l命令,并匹配目录。
代码:
$ ls -l | grep ^d
3、在行尾以$匹配字符串或字符
可以说$与^正相反,它在行尾匹配字符串或字符, $符号放在匹配单词后。
例一:列出文件httpd1.conf中所有以单词common结尾的行
代码:
$grep common$ httpd1.conf
或
$cat httpd1.conf | grep common$
例二:匹配所有空行:^ $
例三:只返回包含一个字符的行:^.$
4、用屏蔽一个特殊字符的含义
下列字符一般可以认为是特殊字符:
代码:
$ . ' " * [] ^ | () + ?
引用:
如:
.
反斜杠后面的字符不再是特殊字符,而是一个普通字符,即句点。
引用:
假定要匹配包含^的各行,将反斜杠放在它前面就可以屏蔽其特殊含义:
^
引用:
在正则表达式中匹配以* . p a s结尾的所有文件:
* . p a s
即可屏蔽字符*的特定含义。
5、使用{}匹配模式结果出现的次数
使用*可匹配所有匹配结果任意次,但如果只要指定次数,就应使用 { },
引用:
此模式有三种形式,即:
pattern{n} 匹配模式出现n次。
pattern{n,} 匹配模式出现最少n次。
pattern{n,m} 匹配模式出现n到m次之间,n , m为0 - 2 5 5中任意整数。
例一:匹配字母A出现两次,并以B结尾:
代码:
A { 2 } B
匹配值为A A B
例二:匹配A至少4次:
代码:
A { 4 , } B
可以得结果A A A A B或A A A A A A A B,但不能为A A A B。
例三:如给出出现次数范围,例如A出现2次到4次之间:
代码:
A { 2 , 4 } B
则结果为A A B、A A A B、A A A A B,而不是A B或A A A A A B等。
例四:假定从下述列表中抽取代码:
引用:
1234XC9088
4523XX9001
0011XA9912
9931Xc3445
格式如下:前4个字符是数字,接下来是x x,最后4个也是数字,操作如下:
[ 0 - 9 ] { 4 }X X[ 0 - 9 ] { 4 }
引用:
具体含义如下:
1) 匹配数字出现4次。
2) 后跟代码x x。
3) 最后是数字出现4次。
结果如下
引用:
1234XC9088 -no match
4523XX9001 -match
0011XA9912 -no match
9931Xc3445 -no match
经常使用的正则表达式举例
代码:
^ 对行首
$ 对行尾
^ [ t h e ] 对以t h e开头行
[ S s ] i g n a [ l L ] 对匹配单词s i g n a l、s i g n a L、S i g n a l、S i g n a L
[Ss]igna[lL]. 对同上,但加一句点
[ m a y M A Y ] 对包含m a y大写或小写字母的行
^ U S E R $ 对只包含U S E R的行
[tty]$ 对以t t y结尾的行
. 对带句点的行
^ d . . x . . x . . x 对对用户、用户组及其他用户组成员有可执行权限的目录
^ [ ^ l ] 对排除关联目录的目录列表
[ . * 0 ] 对0之前或之后加任意字符
[ 0 0 0 * ] 对0 0 0或更多个
[ iI] 对大写或小写I
[ i I ] [ n N ] 对大写或小写i或n
[ ^ $ ] 对空行
[ ^ . * $ ] 对匹配行中任意字符串
^ . . . . . . $ 对包括6个字符的行
[a- zA-Z] 对任意单字符
[ a - z ] [ a - z ] * 对至少一个小写字母
[ ^ 0 - 9 $ ] 对非数字或美元标识
[ ^ 0 - 0 A - Z a - z ] 对非数字或字母
[ 1 2 3 ] 对1到3中一个数字
[ D d ] e v i c e 对单词d e v i c e或D e v i c e
D e . . c e 对前两个字母为D e,后跟两个任意字符,最后为c e
^ q 对以^ q开始行
^ . $ 对仅有一个字符的行
^.[0-9][0-9] 对以一个句点和两个数字开始的行
' " D e v i c e " ' 对单词d e v i c e
D e [ V v ] i c e . 对单词D e v i c e或d e v i c e
[ 0 - 9 ] { 2 } - [ 0 - 9 ] { 2 } - [ 0 - 9 ] { 4 } 对日期格式d d - m m - y y y y
[ 0 - 9 ] { 3 } . [ 0 - 9 ] { 3 } . [ 0 - 9 ] { 3 } . [ 0 - 9 ] { 3 } 对I P地址格式nnn. nnn.nnn.nnn
[ ^ . * $ ] 对匹配任意行
sh
ell基础五:输入和输出(echo,read,cat,管道,tee,重定向等)
从echo的变量开始说起
如:e c h o命令输出转义符以及变量。
代码:
# echo -e "07your home is $HOME , you are connected on `tty`"
your home is /root , you are connected on /dev/pts/1
# echo -e "ayour home is $HOME , you are connected on `tty`"
your home is /root , you are connected on /dev/pts/1
#
引用:
本例中
07或a你可以让终端铃响一声
显示出$ H O M E目录,
并且可以让系统执行t t y命令(注意,该命令用键盘左上角的符号,法语中的抑音符引起来,不是单引号 )。
在e c h o命令输出之后附加换行,可以使用 n选项:
代码:
$ cat echod
#!/bin/sh
echo -e "this echo's 3 new lines "
echo "OK"
编辑一个新echod,如上内容,然后运行输出如下:
代码:
$ ./echod
this echo's 3 new lines
OK
$
在e c h o语句中使用跳格符,记住别忘了加反斜杠:
代码:
$ echo -e "here is a tab here are two tabs ok"
here is a tab here are two tabs ok
$
把一个字符串输出到文件中,使用重定向符号>。
在下面的例子中一个字符串被重定向到一个名为m y f i l e的文件中:
代码:
$ echo "The log files have all been done"> myfile
或者可以追加到一个文件的末尾,这意味着不覆盖原有的内容:
代码:
$ echo "$LOGNAME carried them out at `date`">>myfile
现在让我们看一下m y f i l e文件中的内容:
引用:
The log files have all been done
sam carried them out at 六 11月 13 12:54:32 CST 2004
引号是一个特殊字符,所以必须要使用反斜杠来使s h e l l忽略它的特殊含义。
假设你希望使用e c h o命令输出这样的字符串:“/ d e v / r m t 0”,那么我们只要在引号前面加上反斜杠即可:
代码:
$ echo ""/dev/rmt0""
"/dev/rmt0"
$
其它用法:
--〉'echo'用法收集
tee:读取标准输入的数据,并将其内容输出成文件。
语 法:tee [-ai][--help][--version][文件…]
补充说明:tee指令会从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件。我们可利用tee把管道导入的数据存成文件,甚至一次保存数份文件。
参 数:-a 附加到既有文件的面,而非覆盖它。如果给予tee指令的文件名称已经存在,预设会覆盖该文件的内容。加上此参数,数据会新增在该文件内容的最面,而不会删除原先之内容。
-i 忽略中断信号
--help 在线帮助
--version 显示版本信息
例一:
列出文本文件slayers.story的内容,同时复制3份副本,文件名称分别为ss-copy1、ss-copy2、ss-copy3:
代码:
$ cat slayers.story |tee ss-copy1 ss-copy2 ss-copy3
例一: 把列出当前目录,并把结果结到myfile里
代码:
$ls -l |tee myfile
管道:可以通过管道把一个命令的输出传递给另一个命令作为输入。管道用竖杠|表示。它的一般形式为:
代码:
命令1 |命令2
其中|是管道符号。
上例就是
标准输入、输出和错误
当我们在s h e l l中执行命令的时候,每个进程都和三个打开的文件相联系,并使用文件描述符来引用这些文件。由于文件描述符不容易记忆, s h e l l同时也给出了相应的文件名。
下面就是这些文件描述符及它们通常所对应的文件名:
引用:
文件文件描述符
输入文件—标准输入0:它是命令的输入,缺省是键盘,也可以是文件或其他命令的输出。
输出文件—标准输出1:它是命令的输出,缺省是屏幕,也可以是文件。
错误输出文件—标准错误2:这是命令错误的输出,缺省是屏幕,同样也可以是文件。
如果没有特别指定文件说明符,命令将使用缺省的文件说明符(你的屏幕,更确切地说是你的终端)。
系统中实际上有1 2个文件描述符,但是正如我们在上表中所看到的, 0、1、2是标准输入、输出和错误。可以任意使用文件描述符3到9。
在执行命令时,可以指定命令的标准输入、输出和错误,要实现这一点就需要使用文件重定向。表5 - 1列出了最常用的重定向组合,并给出了相应的文件描述符。
在对标准错误进行重定向时,必须要使用文件描述符,但是对于标准输入和输出来说,这不是必需的。
代码:
常用文件重定向命令
command > filename 把把标准输出重定向到一个新文件中
command >> filename 把把标准输出重定向到一个文件中(追加)
command 1 > fielname 把把标准输出重定向到一个文件中
command > filename 2>&1 把把标准输出和标准错误一起重定向到一个文件中
command 2 > filename 把把标准错误重定向到一个文件中
command 2 >> filename 把把标准输出重定向到一个文件中(追加)
command >> filename 2>&1 把把标准输出和标准错误一起重定向到一个文件中(追加)
command filename2 把c o m m a n d命令以f i l e n a m e文件作为标准输入,以f i l e n a m e 2文件
作为标准输出
command &m 把把标准输出重定向到文件描述符m中
command
例子
[转载]常用文件重定向命令 (这篇网中人的回复好精彩啊^-^)
关于>&2、2>&1等重定向的详细解释!
转贴:UNIX管道和重定向功能在系统备份中的妙用
exec:
e x e c命令可以用来替代当前s h e l l;换句话说,并没有启动子s h e l l。使用这一命令时任何现有环境都将会被清除,并重新启动一个s h e l l。它的一般形式为:
exec command
其中的c o m m a n d通常是一个s h e l l脚本。
我所能够想像得出的描述e x e c命令最贴切的说法就是:当这个脚本结束时,相应的会话可能就结束了。e x e c命令的一个常见用法就是在用户的. p r o f i l e最后执行时,用它来执行一些用于增强安全性的脚本。如果用户的输入无效,该
s h e l l将被关闭,然后重新回到登录提示符。e x e c还常常被用来通过文件描述符打开文件。
e x e c在对文件描述符进行操作的时候(也只有在这时),它不会覆盖你当前的s h e l l。
可以看网中人《shell十三问》第六节:
6) exec 跟 source 差在哪?
能把十三问一次性看完最好,不过对我来说还是有些难度,今天才弄清楚第四问,看了好久才明白,目前为止,看完1,2,3,4,及11
exec:
e x e c命令可以用来替代当前s h e l l;换句话说,并没有启动子s h e l l。使用这一命令时任何现有环境都将会被清除,并重新启动一个s h e l l。它的一般形式为:
exec command
其中的c o m m a n d通常是一个s h e l l脚本。
e x e c在对文件描述符进行操作的时候,它不会覆盖你当前的s h e l l。
这章到此为止了
、变量一定得用""
2、处理顺序要搞清楚:这两行一定要牢牢记在脑中
引用:
命令格式
command-name options argument
处理过程:
shell 会依据 IFS(Internal Field Seperator) 将 command line 所输入的文字给拆解为"字段"(word)。
然后再针对特殊字符(meta)先作处理,
最后再重组整行 command line 。
3、例子:
空格的好理解,但CR字符不好理解,如'',""
代码:
$ A='B
> C
> '
$ echo "$A"
B
C
$ echo $A
B C
echo 的$A加上soft quote后,得出的结果不同了,
第一个是断行字符(new line),取消了CR和IFS的功能
第二个应该是一个空格了,仅取消CR功能,而保留IFS功能
第三个是CR
原因如下:
然而,由于 echo $A 时的变量没至于 soft quote 中,因此当变量替换完成后并作命令行重组时, 会被解释为 IFS (空格键),而不是解释为 New Line (换行符)字符。
而在escape中
代码:
$ A=B
> C
>
$ echo $A
BC
$ echo "$A "
BC
得出的结果是BC,原因:
键本身在 shell meta 中的特殊性,在 跳脱后面,仅仅取消其 CR 功能,而不会保留其 IFS 功能(空格)。 因此就是(NULL)
因此在上面两个例子中 键所产生的字符有四种:
引用:
CR (结束命令)
IFS (空格)
NL(New Line) (断行)
NULL (空)
不知我的理解是否正确,还望各位指点,呵呵,我感觉这样说好像更容易理解
上面理解了,下面就不难了,找个地方放一下,俺怕自己给忘了
而接下来的例子中,则要理解shell meta 与 command meta
有些meta ,都是有特殊用途的,比如 { } ,但在awk中 却要用 { } 来区分出 awk 的命令区段(BEGIN, MAIN, END),也就是双方都用到了{}
如果输入下例命令就会出错:
代码:
$ awk {print $0} 1.txt
这是因为 { } 在 shell 中并没关闭,那 shell 就将 {print $0} 视为command block , 而不是awk的参数,但同时又没有" ; "符号作命令区隔,因此就出现 awk 的语法错误结果。
要解决之,可用 hard quote :
代码:
$ awk '{print $0}' 1.txt
将原本的 {、、$(注三)、} 这几个 shell meta 关闭, 避免掉在 shell 中遭到处理,而完整的成为 awk 参数中的 command meta 。
( 注三:而其中的 $0 是 awk 内建的 field number ,而非 awk 的变量,awk 自身的变量无需使用 $ 。)
要是理解了 hard quote 的功能,再来理解 soft quote 与 escape 就不难:
代码:
awk "{print $0}" 1.txt
awk {print $0} 1.txt
第一行:由于soft quote中没法关闭$,因此用来关闭$meta
第二行:{关闭{, (空格,关闭空格键),$,}就不用说了
如果awk 的 $0 的 0 值是从另一个 shell 变量读进
比方说:已有变量 $A 的值是 0 ,那如何在 command line 中解决 awk 的 $$A 呢? 那么 hard quoe 就不可行了:
代码:
代码:
$ awk '{print $$A}' 1.txt
因为 $A 的 $ 在 hard quote 中是不能替换变量的。
可以使用如下几种方案:
代码:
A=0
awk "{print
A}" 1.txt
awk {print
A} 1.txt
awk '{print $'$A'}' 1.txt
awk '{print $'"$A"'}' 1.txt # 注:"$A" 包在 soft quote 中
上面得注意$和''和""的包含位置
shell基础九:awk
面没有讲述a w k的全部特性,也不涉及a w k的深层次编程,仅讲述使用a w k执行行操作及怎样从文本文件和字符串中抽取信息。
引用:
内容有:
? 抽取域。
? 匹配正则表达式。
? 比较域。
? 向a w k传递参数。
? 基本的a w k行操作和脚本。
a w k语言的最基本功能是在文件或字符串中基于指定规则浏览和抽取信息。a w k抽取信息后,才能进行其他文本操作。完整的a w k脚本通常用来格式化文本文件中的信息。
1 调用awk
有三种方式调用a w k,第一种是命令行方式,如:
代码:
awk [-F fild-separator] 'commands' input-file(s)
这里,c o m m a n d s是真正的a w k命令。
上面例子中, [ - F域分隔符]是可选的,因为a w k使用空格作为缺省的域分隔符,因此如果要浏览域间有空格的文本,不必指定这个选项,但如果要浏览诸如p a s s w d文件,此文件各域以冒号作为分隔符,则必须指明- F选项,如:
代码:
awk -F: 'commands' input-file(s)
第二种方法是将所有a w k命令插入一个文件,并使a w k程序可执行,然后用a w k命令解释器作为脚本的首行,以便通过键入脚本名称来调用它。
第三种方式是将所有的a w k命令插入一个单独文件,然后调用:
代码:
awk -f awk-script-file input-files(s)
- f选项指明在文件a w k _ s c r i p t _ f i l e中的a w k脚本, i n p u t _ f i l e ( s )是使用a w k进行浏览的文件名。
2 awk脚本
在命令中调用a w k时,a w k脚本由各种操作和模式组成。
如果设置了- F选项,则a w k每次读一条记录或一行,并使用指定的分隔符分隔指定域,但如果未设置- F选项,a w k假定空格为域分隔符,并保持这个设置直到发现一新行。当新行出现时,a w k命令获悉已读完整条记录,然后在下一个记录启动读命令,这个读进程将持续到文件尾或文件不再存在。
参照表,a w k每次在文件中读一行,找到域分隔符(这里是符号#),设置其为域n,直至一新行(这里是缺省记录分隔符),然后,划分这一行作为一条记录,接着a w k再次启动下一行读进程。
awk读文件记录的方式
引用:
域1 分隔符 域2 分隔符 域3 分隔符 域4及换行
P. B u n n y (记录1 ) # 0 2 / 9 9 # 4 8 # Yellow
J . Tr o l l (记录2 ) # 0 7 / 9 9 # 4 8 4 2 # Brown-3
2.1 模式和动作
任何a w k语句都由模式和动作组成。在一个a w k脚本中可能有许多语句。模式部分决定动作语句何时触发及触发事件。处理即对数据进行的操作。如果省略模式部分,动作将时刻保持执行状态。
模式可以是任何条件语句或复合语句或正则表达式。模式包括两个特殊字段B E G I N和E N D。使用B E G I N语句设置计数和打印头。B E G I N语句使用在任何文本浏览动作之前,之后文本浏览动作依据输入文件开始执行。E N D语句用来在a w k完成文本浏览动作后打印输出文本总数和结尾状态标志。如果不特别指明模式, a w k总是匹配或打印行数。
实际动作在大括号{ }内指明。动作大多数用来打印,但是还有些更长的代码诸如i f和循环(l o o p i n g)语句及循环退出结构。如果不指明采取动作, a w k将打印出所有浏览出来的记录。
2. 域和记录
a w k执行时,其浏览域标记为$ 1,$ 2 . . . $ n。这种方法称为域标识。使用这些域标识将更容易对域进行进一步处理。
使用$ 1 , $ 3表示参照第1和第3域,注意这里用逗号做域分隔。如果希望打印一个有5个域的记录的所有域,不必指明$ 1 , $ 2 , $ 3 , $ 4 , $ 5,可使用$ 0,意即所有域。Aw k浏览时,到达一新行,即假定到达包含域的记录末尾,然后执行新记录下一行的读动作,并重新设置域分隔。
注意执行时不要混淆符号$和s h e l l提示符$,它们是不同的。
为打印一个域或所有域,使用p r i n t命令。这是一个a w k动作(动作语法用圆括号括起来)。
1. 抽取域
真正执行前看几个例子,现有一文本文件g r a d e . t x t,记录了一个称为柔道数据库的行信息。
代码:
$ cat grade.txt
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99 4712 Brown-2 12 30 28
此文本文件有7个域,即(1)名字、(2)升段日期、(3)学生序号、(4)腰带级别、(5)年龄、(6)目前比赛积分、(7)比赛最高分。
因为域间使用空格作为域分隔符,故不必用- F选项划分域,现浏览文件并导出一些数据。在例子中为了利于显示,将空格加宽使各域看得更清晰。
2. 保存a w k输出
有两种方式保存s h e l l提示符下a w k脚本的输出。最简单的方式是使用输出重定向符号>文件名,下面的例子重定向输出到文件w o w。
代码:
$ awk '{print $0}' grade.txt >wow
$ cat grade.txt
使用这种方法要注意,显示屏上不会显示输出结果。因为它直接输出到文件。只有在保证输出结果正确时才会使用这种方法。它也会重写硬盘上同名数据。
第二种方法是使用t e e命令,在输出到文件的同时输出到屏幕。在测试输出结果正确与否时多使用这种方法。例如输出重定向到文件d e l e t e _ m e _ a n d _ d i e,同时输出到屏幕。使用这种方法,在a w k命令结尾写入| tee delete_me_and_die。
代码:
$ awk '{print $0}' grade.txt | tee delete_me_and_die
3. 使用标准输入
在深入讲解这一章之前,先对a w k脚本的输入方法简要介绍一下。实际上任何脚本都是从标准输入中接受输入的。为运行本章脚本,使用a w k脚本输入文件格式,例如:
引用:
belts.awk grade_student.txt
也可替代使用下述格式:
使用重定向方法:
belts.awk
这里我怎么看不明白,汗
4. 打印所有记录
代码:
$ awk '{print $0}' grade.txt
a w k读每一条记录。因为没有模式部分,只有动作部分{print $0}(打印所有记录),这个动作必须用花括号括起来。上述命令打印整个文件。
5. 打印单独记录
假定只打印学生名字和腰带级别,通过查看域所在列,可知为f i e l d - 1和f i e l d - 4,因此可以使用$ 1和$ 4,但不要忘了加逗号以分隔域。
代码:
$ awk '{print $1,$4}' grade.txt
M.Tans Green
J.Lulu green
P.Bunny Yellow
J.Troll Brown-3
L.Tansl Brown-2
6. 打印报告头
上述命令输出在名字和腰带级别之间用一些空格使之更容易划分,也可以在域间使用t a b键加以划分。为加入t a b键,使用t a b键速记引用符 t,后面将对速记引用加以详细讨论。也可以为输出文本加入信息头。本例中加入n a m e和b e l t及下划线。下划线使用 n,强迫启动新行,并在 n下一行启动打印文本操作。打印信息头放置在B E G I N模式部分,因为打印信息头被界定为一个动作,必须用大括号括起来。在a w k查看第一条记录前,信息头被打印。
代码:
$ awk 'BEGIN {print "Name Belt -----------------------------------"}{print $1" ",$4}' grade.txt
Name Belt
-----------------------------------
M.Tans Green
J.Lulu green
P.Bunny Yellow
J.Troll Brown-3
L.Tansl Brown-2
7. 打印信息尾
如果在末行加入end of report信息,可使用E N D语句。E N D语句在所有文本处理动作执行完之后才被执行。E N D语句在脚本中的位置放置在主要动作之后。下面简单打印头信息并告之查询动作完成。
代码:
$ awk 'BEGIN {print "Name --------"}{print $1} END {"end-of-report"}' grade.txt
Name
--------
M.Tans
J.Lulu
P.Bunny
J.Troll
L.Tansl
8. awk错误信息提示
几乎可以肯定,在使用a w k时,将会在命令中碰到一些错误。a w k将试图打印错误行,但由于大部分命令都只在一行,因此帮助不大。
系统给出的显示错误信息提示可读性不好。使用上述例子,如果丢了一个双引号, a w k将返回:
代码:
$ awk 'BEGIN {print "Name --------}{print $1} END {"end-of-report"}' grade.txt
awk: cmd. line:1: BEGIN {print "Name --------}{print $1} END {"end-of-report"}
awk: cmd. line:1: ^ unterminated string
当第一次使用a w k时,可能被错误信息搅得不知所措,但通过长时间和不断的学习,可总结出以下规则。在碰到a w k错误时,可相应查找:
引用:
& #8226; 确保整个a w k命令用单引号括起来。
? 确保命令内所有引号成对出现。
? 确保用花括号括起动作语句,用圆括号括起条件语句。
? 可能忘记使用花括号,也许你认为没有必要,但a w k不这样认为,将按之解释语法。
如果查询文件不存在,将得到下述错误信息:
代码:
$ awk 'END {print NR}' grades.txt
awk: cmd. line:2: fatal: cannot open file `grades.txt' for reading (没有那个文件或目录)
9.awk 键盘输入
如果在命令行并没有输入文件g r a d e . t x t,将会怎样?
代码:
$ awk 'BEGIN {print "Name --------"}{print $1} END {"end-of-report"}'
Name
--------
B E G I N部分打印了文件头,但a w k最终停止操作并等待,并没有返回s h e l l提示符。这是因为a w k期望获得键盘输入。因为没有给出输入文件, a w k假定下面将会给出。如果愿意,顺序输入相关文本,并在输入完成后敲键。如果敲入了正确的域分隔符, a w k会像第一个例子一样正常处理文本。这种处理并不常用,因为它大多应用于大量的打印稿。
2.3awk中正则表达式及其操作
在g r e p一章中,有许多例子用到正则表达式,这里将不使用同样的例子,但可以使用条件操作讲述a w k中正则表达式的用法。
这里正则表达式用斜线括起来。例如,在文本文件中查询字符串G r e e n,使用/ G r e e n /可以查出单词G r e e n的出现情况。
2.4元字符
这里是a w k中正则表达式匹配操作中经常用到的字符,详细情况请参阅本书第7章正则表达式概述。
代码:
^ $ . [] | () * + ?
这里有两个字符第7章没有讲到,因为它们只适用于a w k而不适用于g r e p或s e d。它们是:
引用:
+ 使用+匹配一个或多个字符。
? 匹配模式出现频率。例如使用/X Y?Z/匹配X Y Z或Y Z。
条件操作符
a w k条件操作符
操作符描述操作符描述
= 大于等于
1. 匹配
为使一域号匹配正则表达式,使用符号‘~’后紧跟正则表达式,也可以用i f语句。a w k中i f后面的条件用()括起来。
观察文件g r a d e . t x t,如果只要显示b r o w n腰带级别可知其所在域为f i e l d - 4,这样可以写出表达式{if($4~/brown/) print }意即如果f i e l d - 4包含b r o w n,打印它。如果条件满足,则打印匹配记录行。可以编写下面脚本,因为这是一个动作,必须用花括号{ }括起来。
代码:
[root@Linux_chenwy sam]# awk '{if($4~/Brown/) print $0}' grade.txt
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99 4712 Brown-2 12 30 28
匹配记录找到时,如果不特别声明, a w k缺省打印整条记录。使用i f语句开始有点难,但不要着急,因为有许多方法可以跳过它,并仍保持同样结果。下面例子意即如果记录包含模式b r o w n,就打印它:
代码:
[root@Linux_chenwy sam]# awk '$0 ~ /Brown/' grade.txt
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99 4712 Brown-2 12 30 28
2. 精确匹配
假定要使字符串精确匹配,比如说查看学生序号4 8,文件中有许多学生序号包含4 8,如果在f i e l d - 3中查询序号4 8,a w k将返回所有序号带4 8的记录:
代码:
[root@Linux_chenwy sam]# awk '{if($3~/48/) print$0}' grade.txt
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26 26
为精确匹配4 8,使用等号= =,并用单引号括起条件。例如$ 3
代码:
[root@Linux_chenwy sam]# awk '$3=="48" {print$0}' grade.txt
P.Bunny 02/99 48 Yellow 12 35 28
[root@Linux_chenwy sam]# awk '{if($3=="48") print$0}' grade.txt
P.Bunny 02/99 48 Yellow 12 35 28
3. 不匹配
有时要浏览信息并抽取不匹配操作的记录,与~相反的符号是!~,意即不匹配。像原来使用查询b r o w n腰带级别的匹配操作一样,现在看看不匹配情况。表达式$0 !~/brown/,意即查询不包含模式b r o w n腰带级别的记录并打印它。
注意,缺省情况下, a w k将打印所有匹配记录,因此这里不必加入动作部分。
代码:
[root@Linux_chenwy sam]# awk '$0 !~ /Brown/' grade.txt
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28
可以只对f i e l d - 4进行不匹配操作,方法如下:
代码:
[root@Linux_chenwy sam]# awk '{if($4~/Brown/) print $0}' grade.txt
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99 4712 Brown-2 12 30 28
如果只使用命令awk$4 !="brown"{print $0} grade.txt,将返回错误结果,因为用引号括起了b r o w n,将只匹配‘b r o w n而不匹配b r o w n - 2和b r o w n - 3,当然,如果想要查询非b r o w n - 2的腰带级别,可做如下操作:
代码:
[root@Linux_chenwy sam]# awk '$4!="Brown-2" {print $0}' grade.txt
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26 26
4. 小于
看看哪些学生可以获得升段机会。测试这一点即判断目前级别分f i e l d - 6是否小于最高分f i e l d - 7,在输出结果中,加入这一改动很容易。
代码:
[root@Linux_chenwy sam]# awk '{if($6
5. 小于等于
对比小于,小于等于只在操作符上做些小改动,满足此条件的记录也包括上面例子中的输出情况。
代码:
[root@Linux_chenwy sam]# awk '{if($6
6. 大于
代码:
[root@Linux_chenwy sam]# awk '{if($6 > $7) print $1}' grade.txt
P.Bunny
L.Tansl
7. 设置大小写
为查询大小写信息,可使用[ ]符号。在测试正则表达式时提到可匹配[ ]内任意字符或单词,因此若查询文件中级别为g r e e n的所有记录,不论其大小写,表达式应为‘ / [ G g ] r e e n /’
代码:
[root@Linux_chenwy sam]# awk '/[Gg]reen/' grade.txt
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
8. 任意字符
抽取名字,其记录第一域的第四个字符是a,使用句点.。表达式/ ^ . . . a /意为行首前三个字符任意,第四个是a,尖角符号代表行首。
代码:
[root@Linux_chenwy sam]# awk '$1 ~ /^...a/' grade.txt
M.Tans 5/99 48311 Green 8 40 44
L.Tansl 05/99 4712 Brown-2 12 30 28
9. 或关系匹配
为抽取级别为y e l l o w或b r o w n的记录,使用竖线符|。意为匹配| 两边模式之一。注意,使用竖线符时,语句必须用圆括号括起来。
代码:
[root@Linux_chenwy sam]# awk '$0 ~/(Yellow|Brown)/' grade.txt
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99 4712 Brown-2 12 30 28
上面例子输出所有级别为Ye l l o w或B r o w n的记录。
使用这种方法在查询级别为G r e e n或g r e e n时,可以得到与使用[ ]表达式相同的结果。
代码:
[root@Linux_chenwy sam]# awk '/^M/' grade.txt
M.Tans 5/99 48311 Green 8 40 44
10. 行首
不必总是使用域号。如果查询文本文件行首包含M的代码,可简单使用下面^符号:
代码:
[root@Linux_chenwy sam]# awk '/^M/' grade.txt
复合表达式即为模式间通过使用下述各表达式互相结合起来的表达式:
引用:
&& AND : 语句两边必须同时匹配为真。
|| O R:语句两边同时或其中一边匹配为真。
! 非求逆
11. AND
打印记录,使其名字为‘ P. B u n n y且级别为Ye l l o w,使用表达式( $ 1 = = " P. B u n n y " & &$ 4 = = " Ye l l o w " ),意为& &两边匹配均为真。完整命令如下:
代码:
[root@Linux_chenwy sam]# awk '{if ($1=="P.Bunny" && $4=="Yellow") print $0}' grade.txt
P.Bunny 02/99 48 Yellow 12 35 28
12. Or
如果查询级别为Ye l l o w或B r o w n,使用或命令。意为“ | |”符号两边的匹配模式之一或全部为真。
代码:
[root@Linux_chenwy sam]# awk '{if ($4=="Yellow" || $4~/Brown/) print $0}' grade.txt
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99 4712 Brown-2 12 30 28
原来不一定得加print,下面我自己对例一二做了一下
代码:
1
[root@Linux_chenwy sam]# awk '$4~/Brown/' grade.txt
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99 4712 Brown-2 12 30 28
代码:
2
[root@Linux_chenwy sam]# awk '$3=="48"' grade.txt
P.Bunny 02/99 48 Yellow 12 35 28
代码:
[root@Linux_chenwy sam]# awk '$3="48"' grade.txt
M.Tans 5/99 48 Green 8 40 44
J.Lulu 06/99 48 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 48 Brown-3 12 26 26
L.Tansl 05/99 48 Brown-2 12 30 28
2中,我把=和==写错了,呵呵,一个是赋值,一个是等于
awk内置变量
a w k有许多内置变量用来设置环境信息。这些变量可以被改变。表9 - 3显示了最常使用的一些变量,并给出其基本含义。
引用:
awk内置变量
A R G C 命令行参数个数
A R G V 命令行参数排列
E N V I R O N 支持队列中系统环境变量的使用
FILENAME a w k浏览的文件名
F N R 浏览文件的记录数
F S 设置输入域分隔符,等价于命令行- F选项
N F 浏览记录的域个数
N R 已读的记录数
O F S 输出域分隔符
O R S 输出记录分隔符
R S 控制记录分隔符
引用:
A R G C支持命令行中传入a w k脚本的参数个数。A R G V是A R G C的参数排列数组,其中每一元素表示为A R G V [ n ],n为期望访问的命令行参数。
E N V I R O N 支持系统设置的环境变量,要访问单独变量,使用实际变量名,例如E N V I R O N [“E D I TO R”] =“Vi”。
F I L E N A M E支持a w k脚本实际操作的输入文件。因为a w k可以同时处理许多文件,因此如果访问了这个变量,将告之系统目前正在浏览的实际文件。
F N R支持a w k目前操作的记录数。其变量值小于等于N R。如果脚本正在访问许多文件,每一新输入文件都将重新设置此变量。
F S用来在a w k中设置域分隔符,与命令行中- F选项功能相同。缺省情况下为空格。如果用逗号来作域分隔符,设置F S = ","。
N F支持记录域个数,在记录被读之后再设置。
O F S允许指定输出域分隔符,缺省为空格。如果想设置为#,写入O F S = " # "。
O R S为输出记录分隔符,缺省为新行( n)。
R S是记录分隔符,缺省为新行( n )。
NF、NR和FILENAME
要快速查看记录个数,应使用N R。比如说导出一个数据库文件后,如果想快速浏览记录个数,以便对比于其初始状态,查出导出过程中出现的错误。使用N R将打印输入文件的记录个数。print NR放在E N D语法中。
代码:
[root@chenwy sam]# awk 'END{print NR}' grade.txt
5
如:所有学生记录被打印,并带有其记录号。使用N F变量显示每一条读记录中有多少个域,并在E N D部分打印输入文件名。
[root@chenwy sam]# awk '{print NF,NR,$0} END{print FILENAME}' grade.txt
代码:
7 1 M.Tans 5/99 48311 Green 8 40 44
7 2 J.Lulu 06/99 48317 green 9 24 26
7 3 P.Bunny 02/99 48 Yellow 12 35 28
7 4 J.Troll 07/99 4842 Brown-3 12 26 26
7 5 L.Tansl 05/99 4712 Brown-2 12 30 28
grade.txt
在从文件中抽取信息时,最好首先检查文件中是否有记录。下面的例子只有在文件中至少有一个记录时才查询B r o w n级别记录。使用A N D复合语句实现这一功能。意即至少存在一个记录后,查询字符串B r o w n,最后打印结果。
代码:
[root@chenwy sam]# awk '{if (NR>0 && $4~/Brown/)print $0}' grade.txt
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99 4712 Brown-2 12 30 28
N F的一个强大功能是将变量$ P W D的返回值传入a w k并显示其目录。这里需要指定域分隔符/。
代码:
[root@chenwy sam]# echo $PWD | awk -F/ ' {print $NF}'
sam
另一个例子是显示文件名。
代码:
[root@chenwy sam]# echo "/usr/local/etc/rc.sybase" | awk -F/ '{print $NF}'
rc.sybase
如果不指定域分割符,返回的如下:
代码:
[root@chenwy sam]# echo $PWD | awk '{print $NF}'
/usr/sam
[root@chenwy sam]# echo "/usr/local/etc/rc.sybase" | awk '{print $NF}'
/usr/local/etc/rc.sybase
awk操作符
在a w k中使用操作符,基本表达式可以划分为数字型、字符串型、变量型、域及数组元素,前面已经讲过一些。下面列出其完整列表。
在表达式中可以使用下述任何一种操作符。
引用:
= += *= / = %= ^ = 赋值操作符
? 条件表达操作符
|| && ! 并、与、非(上一节已讲到)
~!~ 匹配操作符,包括匹配和不匹配
> 关系操作符
+ - * / % ^ 算术操作符
+ + -- 前缀和后缀
前面已经讲到了其中几种操作,下面继续讲述未涉及的部分。
1. 设置输入域到域变量名
在a w k中,设置有意义的域名是一种好习惯,在进行模式匹配或关系操作时更容易理解。
一般的变量名设置方式为n a m e = $ n,这里n a m e为调用的域变量名, n为实际域号。例如设置学生域名为n a m e,级别域名为b e l t,操作为n a m e = $ 1 ; b e l t s = $ 4。注意分号的使用,它分隔a w k命令。下面例子中,重新赋值学生名域为n a m e,级别域为b e l t s。查询级别为Ye l l o w的记录,并最终打印名称和级别。
代码:
[sam@chenwy sam]$ awk '{name=$1;belts=$4;if(belts ~/Yellow/) print name" is belt "belts}' grade.txt
P.Bunny is belt Yellow
2. 域值比较操作
有两种方式测试一数值域是否小于另一数值域。
1) 在B E G I N中给变量名赋值。
2) 在关系操作中使用实际数值。
通常在B E G I N部分赋值是很有益的,可以在a w k表达式进行改动时减少很多麻烦。
使用关系操作必须用圆括号括起来。
下面的例子查询所有比赛中得分在2 7点以下的学生。
用引号将数字引用起来是可选的,“2 7”、2 7产生同样的结果。
代码:
[sam@chenwy sam]$ awk '{if ($6
第二个例子中给数字赋以变量名B A S E L I N E和在B E G I N部分给变量赋值,两者意义相同。
代码:
[sam@chenwy sam]$ awk 'BEGIN{BASELINE="27"} {if ($6
3. 修改数值域取值
当在a w k中修改任何域时,重要的一点是要记住实际输入文件是不可修改的,修改的只是保存在缓存里的a w k复本。a w k会在变量N R或N F变量中反映出修改痕迹。
为修改数值域,简单的给域标识重赋新值,如: $ 1 = $ 1 + 5,会将域1数值加5,但要确保赋值域其子集为数值型。
修改M . Ta n s l e y的目前级别分域,使其数值从4 0减为3 9,使用赋值语句$ 6 = $ 6 - 1,当然在实施修改前首先要匹配域名。
代码:
[sam@chenwy sam]$ awk '{if($1=="M.Tans") {$6=$6-1};print $1,$6,$7}' grade.txt
M.Tans 39 44
J.Lulu 24 26
P.Bunny 35 28
J.Troll 26 26
L.Tansl 30 28
代码:
[sam@chenwy sam]$ awk '{if($1=="M.Tans") {$6=$6-1;print $1,$6,$7}}' grade.txt
M.Tans 39 44
4. 修改文本域
修改文本域即对其重新赋值。需要做的就是赋给一个新的字符串。在J . Tr o l l中加入字母,使其成为J . L . Tr o l l,表达式为$ 1 = " J . L . Tr o l l ",记住字符串要使用双秒号( " "),并用圆括号括起整个语法。
代码:
[sam@chenwy sam]$ awk '{if($1=="J.Troll") $1="J.L.Troll"; print $1}' grade.txt
M.Tans
J.Lulu
P.Bunny
J.L.Troll
L.Tansl
5. 只显示修改记录
上述例子均是对一个小文件的域进行修改,因此打印出所有记录查看修改部分不成问题,但如果文件很大,记录甚至超过1 0 0,打印所有记录只为查看修改部分显然不合情理。在模式后面使用花括号将只打印修改部分。取得模式,再根据模式结果实施操作,可能有些抽象,现举一例,只打印修改部分。注意花括号的位置。
代码:
[sam@chenwy sam]$ awk '{if($1=="J.Troll") {$1="J.L.Troll"; print $1}}' grade.txt
J.L.Troll
不知道为什么,我这里多了一个空行?
6. 创建新的输出域
在a w k中处理数据时,基于各域进行计算时创建新域是一种好习惯。创建新域要通过其他域赋予新域标识符。如创建一个基于其他域的加法新域{ $ 4 = $ 2 + $ 3 },这里假定记录包含3个域,则域4为新建域,保存域2和域3相加结果。
在文件g r a d e . t x t中创建新域8保存域目前级别分与域最高级别分的减法值。表达式为‘{ $ 8 = $ 7 - $ 6 }’,语法首先测试域目前级别分小于域最高级别分。新域因此只打印其值大于零的学生名称及其新域值。在B E G I N部分加入t a b键以对齐报告头。
代码:
[sam@chenwy sam]$ awk 'BEGIN{print "Name Difference"}{if($6
当然可以创建新域,并赋给其更有意义的变量名。例如:
代码:
[sam@chenwy sam]$ awk 'BEGIN{print "Name Difference"}{if($6
7. 增加列值
为增加列数或进行运行结果统计,使用符号+ =。增加的结果赋给符号左边变量值,增加到变量的域在符号右边。例如将$ 1加入变量t o t a l,表达式为t o t a l + = $ 1。列值增加很有用。许多文件都要求统计总数,但输出其统计结果十分繁琐。在a w k中这很简单,请看下面的例子。
将所有学生的‘目前级别分’加在一起,方法是t o t + = $ 6,t o t即为a w k浏览的整个文件的域6结果总和。所有记录读完后,在E N D部分加入一些提示信息及域6总和。不必在a w k中显示说明打印所有记录,每一个操作匹配时,这是缺省动作。
代码:
[sam@chenwy sam]$ awk '(tot+=$6); END{print "Club student total points :" tot}'
grade.txt
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99 4712 Brown-2 12 30 28
Club student total points :155
如果文件很大,你只想打印结果部分而不是所有记录,在语句的外面加上圆括号()即可。
代码:
[sam@chenwy sam]$ awk '{(tot+=$6)}; END{print "Club student total points :" tot}' grade.txt
Club student total points :155
8. 文件长度相加
在目录中查看文件时,如果想快速查看所有文件的长度及其总和,但要排除子目录,使用ls -l命令,然后管道输出到a w k,a w k首先剔除首字符为d(使用正则表达式)的记录,然后将文件长度列相加,并输出每一文件长度及在E N D部分输出所有文件的长度。
本例中,首先用ls -l命令查看一下文件属性。注意第二个文件属性首字符为d,说明它是一个目录,文件长度是第5列,文件名是第9列。如果系统不是这样排列文件名及其长度,应适时加以改变。
下面的正则表达式表明必须匹配行首,并排除字符d,表达式为^ [ ^ d ]。
使用此模式打印文件名及其长度,然后将各长度相加放入变量t o t中。
代码:
[sam@chenwy sam]$ ls -l | awk '/^[^d]/ {print $9" "$5} {tot+=$5} END {print "total KB:" tot}'
...................
total KB:174144
内置的字符串函数
代码:
awk内置字符串函数
g s u b ( r, s ) 在整个$ 0中用s替代r
g s u b ( r, s , t ) 在整个t中用s替代r
i n d e x ( s , t ) 返回s中字符串t的第一位置
l e n g t h ( s ) 返回s长度
m a t c h ( s , r ) 测试s是否包含匹配r的字符串
s p l i t ( s , a , f s ) 在f s上将s分成序列a
s p r i n t ( f m t , e x p ) 返回经f m t格式化后的e x p
s u b ( r, s ) 用$ 0中最左边最长的子串代替s
s u b s t r ( s , p ) 返回字符串s中从p开始的后缀部分
s u b s t r ( s , p , n ) 返回字符串s中从p开始长度为n的后缀部分
g s u b函数有点类似于s e d查找和替换。它允许替换一个字符串或字符为另一个字符串或字符,并以正则表达式的形式执行。第一个函数作用于记录$ 0,第二个g s u b函数允许指定目标,然而,如果未指定目标,缺省为$ 0。
i n d e x(s,t)函数返回目标字符串s中查询字符串t的首位置。l e n g t h函数返回字符串s字符长度。
m a t c h函数测试字符串s是否包含一个正则表达式r定义的匹配。s p l i t使用域分隔符f s将字符串s划分为指定序列a。
s p r i n t函数类似于p r i n t f函数(以后涉及),返回基本输出格式f m t的结果字符串e x p。
s u b(r,s)函数将用s替代$ 0中最左边最长的子串,该子串被( r)匹配。
s u b(s,p)返回字符串s在位置p后的后缀。s u b s t r(s,p,n)同上,并指定子串长度为n。
现在看一看a w k中这些字符串函数的功能。
1. gsub
要在整个记录中替换一个字符串为另一个,使用正则表达式格式, /目标模式/,替换模式/。例如改变学生序号4 8 4 2到4 8 9 9:
代码:
[root@Linux_chenwy root]# cd /usr/sam
[root@Linux_chenwy sam]# awk 'gsub(/4842/,4899){print $0}' grade.txt
J.Troll 07/99 4899 Brown-3 12 26 26
代码:
[root@Linux_chenwy sam]# awk 'gsub(/4842/,4899)' grade.txt
J.Troll 07/99 4899 Brown-3 12 26 26
2. index
查询字符串s中t出现的第一位置。必须用双引号将字符串括起来。例如返回目标字符串B u n n y中n y出现的第一位置,即字符个数。
代码:
[root@Linux_chenwy sam]# awk 'BEGIN {print index("Bunny","ny")}' grade.txt
4
3. length
返回所需字符串长度,例如检验字符串J . Tr o l l返回名字及其长度,即人名构成的字符个数
代码:
[root@Linux_chenwy sam]# awk '$1=="J.Troll" {print length($1)" "$1}' grade.txt
7 J.Troll
还有一种方法,这里字符串加双引号。
代码:
[root@Linux_chenwy sam]# awk 'BEGIN{print length("A FEW GOOD MEN")}'
14
4. match
m a t c h测试目标字符串是否包含查找字符的一部分。可以对查找部分使用正则表达式,返回值为成功出现的字符排列数。如果未找到,返回0,第一个例子在A N C D中查找d。因其不存在,所以返回0。第二个例子在A N C D中查找D。因其存在,所以返回A N C D中D出现的首位置字符数。第三个例子在学生J . L u l u中查找u。
代码:
[root@Linux_chenwy sam]# awk 'BEGIN{print match("ANCD",/d/)}'
0
[root@Linux_chenwy sam]# awk 'BEGIN{print match("ANCD",/D/)}'
4
[root@Linux_chenwy sam]# awk '$1=="J.Lulu" {print match($1,"u")}' grade.txt
4
5. split
使用s p l i t返回字符串数组元素个数。工作方式如下:如果有一字符串,包含一指定分隔符- ,例如A D2 - K P 9 - J U 2 - L P - 1,将之划分成一个数组。使用s p l i t,指定分隔符及数组名。此例中,命令格式为( " A D 2 - K P 9 - J U 2 - L P - 1 ",p a r t s _ a r r a y," - "),s p l i t然后返回数组下标数,这里结果为4。
代码:
[root@Linux_chenwy sam]# awk 'BEGIN {print split("123-456-789",pats_array,"-")}'3
还有一个例子使用不同的分隔符。
代码:
[root@Linux_chenwy sam]# awk 'BEGIN {print split("123#456#789",myarray,"#")}' 3
这个例子中,s p l i t返回数组m y a r r a y的下标数。数组m y a r r a y取值如下:
代码:
myarray[1]=123
myarray[2]=456
myarray[3]=789
结尾部分讲述数组概念。
6. sub
使用s u b发现并替换模式的第一次出现位置。字符串S T R包含‘poped popo pill’,执行下列s u b命令s u b(/ o p /," o p ",S T R)。模式o p第一次出现时,进行替换操作,返回结果如下:‘pO Ped pope pill’。
如:学生J . Tr o l l的记录有两个值一样,“目前级别分”与“最高级别分”。只改变第一个为2 9,第二个仍为2 4不动,操作命令为s u b(/ 2 6 /," 2 9 ",$ 0),只替换第一个出现2 4的位置。注意J . Tr o l l记录需存在。
代码:
[root@Linux_chenwy sam]# awk '$1=="J.Troll" sub(/26/,"29",$0)' grade.txt
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 29
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 29 26
L.Tansl 05/99 4712 Brown-2 12 30 28
7. substr
s u b s t r是一个很有用的函数。它按照起始位置及长度返回字符串的一部分。例子如下:
代码:
[root@Linux_chenwy sam]# awk '$1=="L.Tansl" {print substr($1,1,3)}' grade.txt
L.T
上面例子中,指定在域1的第一个字符开始,返回其前面5个字符。
如果给定长度值远大于字符串长度, a w k将从起始位置返回所有字符,要抽取L Ta n s l - e y的姓,只需从第3个字符开始返回长度为7。可以输入长度9 9,a w k返回结果相同。
代码:
[root@Linux_chenwy sam]# awk '$1=="L.Tansl" {print substr($1,1,99)}' grade.txt
L.Tansl
s u b s t r的另一种形式是返回字符串后缀或指定位置后面字符。这里需要给出指定字符串及其返回字串的起始位置。例如,从文本文件中抽取姓氏,需操作域1,并从第三个字符开始:
代码:
[root@Linux_chenwy sam]# awk '{print substr($1,3)}' grade.txt
Tans
Lulu
Bunny
Troll
Tansl
还有一个例子,在B E G I N部分定义字符串,在E N D部分返回从第t个字符开始抽取的子串。
代码:
[root@Linux_chenwy sam]# awk 'BEGIN{STR="A FEW GOOD MEN"}END{print substr(STR,7)}' grade.txt
GOOD MEN
8. 从s h e l l中向a w k传入字符串
a w k脚本大多只有一行,其中很少是字符串表示的。大多要求在一行内完成a w k脚本,这一点通过将变量传入a w k命令行会变得很容易。现就其基本原理讲
述一些例子。
使用管道将字符串s t a n d - b y传入a w k,返回其长度。
代码:
[root@Linux_chenwy sam]# echo "Stand-by" | awk '{print length($0)}'
8
设置文件名为一变量,管道输出到a w k,返回不带扩展名的文件名。
代码:
[root@Linux_chenwy sam]# STR="mydoc.txt"
[root@Linux_chenwy sam]# echo $STR|awk '{print substr($STR,1,5)}'
mydoc
设置文件名为一变量,管道输出到a w k,只返回其扩展名。
代码:
[root@Linux_chenwy sam]# STR="mydoc.txt"
[root@Linux_chenwy sam]# echo $STR|awk '{print substr($STR,7)}'
txt
字符串屏蔽序列
使用字符串或正则表达式时,有时需要在输出中加入一新行或查询一元字符。
打印一新行时,(新行为字符 n),给出其屏蔽序列,以不失其特殊含义,用法为在字符串前加入反斜线。例如使用 n强迫打印一新行。
如果使用正则表达式,查询花括号( { }),在字符前加反斜线,如/ { /,将在a w k中失掉其特殊含义。
代码:
awk中使用的屏蔽序列
b 退格键
t t a b键
f 走纸换页
d d d 八进制值
n 新行
c 任意其他特殊字符,例如 为反斜线符号
r 回车键
使用上述符号,打印May Day,中间夹t a b键,后跟两个新行,再打印May Day,但这次使用八进制数1 0 4、1 4 1、1 7 1、分别代表D、a、y。
代码:
[root@chenwy sam]# awk 'BEGIN {print" May Day May 104141171"}'
May Day
May Day
注意, 1 0 4为D的八进制A S C I I码, 1 4 1为a的八进制A S C I I码,等等。
awk输出函数printf
目前为止,所有例子的输出都是直接到屏幕,除了t a b键以外没有任何格式。a w k提供函数p r i n t f,拥有几种不同的格式化输出功能。例如按列输出、左对齐或右对齐方式。
每一种p r i n t f函数(格式控制字符)都以一个%符号开始,以一个决定转换的字符结束.转换包含三种修饰符。
p r i n t f函数基本语法是p r i n t f([格式控制符],参数),格式控制字符通常在引号里。
printf修饰符
代码:
- 左对齐
Wi d t h 域的步长,用0表示0步长
. p r e c 最大字符串长度,或小数点右边的位数
表9-7 awk printf格式
% c A S C I I字符
% d 整数
% e 浮点数,科学记数法
% f 浮点数,例如(1 2 3 . 4 4)
% g a w k决定使用哪种浮点数转换e或者f
% o 八进制数
% s 字符串
% x 十六进制数
1. 字符转换
观察A S C I I码中6 5的等价值。管道输出6 5到a w k。p r i n t f进行A S C I I码字符转换。这里也加入换行,因为缺省情况下p r i n t f不做换行动作。
代码:
A[sam@chenwy sam]$ echo "65" | awk '{printf "%c ",$0}'
A
按同样方式使用a w k得到同样结果。
代码:
[sam@chenwy sam]$ awk 'BEGIN{printf "%c ",65}'
A
所有的字符转换都是一样的,下面的例子表示进行浮点数转换后‘ 9 9 9’的输出结果。整数传入后被加了六个小数点。
代码:
[sam@chenwy sam]$ awk 'BEGIN{printf "%f ",999}'
999.000000
2. 格式化输出
打印所有的学生名字和序列号,要求名字左对齐, 1 5个字符长度,后跟序列号。注意 n换行符放在最后一个指示符后面。输出将自动分成两列。
代码:
[root@chenwy sam]# awk '{printf "%-15s %s ",$1,$3}' grade.txt
M.Tans 48311
J.Lulu 48317
P.Bunny 48
J.Troll 4842
L.Tansl 4712
加入一些文本注释帮助理解报文含义。可在正文前嵌入头信息。注意这里使用p r i n t加入头信息。如果愿意,也可使用p r i n t f。
代码:
[root@chenwy sam]# awk 'BEGIN{print "Name S.Number"}{printf "%-15s %s ",$1,$3}' grade.txt
Name S.Number
M.Tans 48311
J.Lulu 48317
P.Bunny 48
J.Troll 4842
L.Tansl 4712
3.向一行a w k命令传值
在查看a w k脚本前,先来查看怎样在a w k命令行中传递变量。
在a w k执行前将值传入a w k变量,需要将变量放在命令行中,格式如下:
代码:
awk 命令变量=输入文件值
(后面会讲到怎样传递变量到a w k脚本中)。
下面的例子在命令行中设置变量A G E等于1 0,然后传入a w k中,查询年龄在1 0岁以下的所有学生。
代码:
[root@chenwy sam]# awk '{if ($5
要快速查看文件系统空间容量,观察其是否达到一定水平,可使用下面a w k一行脚本。因为要监视的已使用空间容量不断在变化,可以在命令行指定一个触发值。首先用管道命令将df -k 传入a w k,然后抽出第4列,即剩余可利用空间容量。使用$ 4 ~ / ^ [ 0 - 9 ] /取得容量数值(1 0 2 4块)而不是d f的文件头,然后对命令行与‘ i f ( $ 4
代码:
[root@chenwy sam]# df -k|awk '{if($4
代码:
[root@chenwy sam]# df -k|awk '($4~/^[0-9]/) {if($4
($4~/^[0-9]/)好像没什么用
在系统中使用df -k命令,产生下列信息:
代码:
[root@chenwy sam]# df -k
文件系统 1K-块 已用 可用 已用% 挂载点
/dev/sda2 5162828 2289804 2610764 47% /
/dev/sda1 497829 13538 458589 3% /boot
none 99352 0 99352 0% /dev/shm
如果系统中d f输出格式不同,必须相应改变列号以适应工作系统。
当然可以使用管道将值传入a w k。本例使用w h o命令, w h o命令第一列包含注册用户名,这里打印注册用户,并加入一定信息。
代码:
[sam@chenwy sam]$ who |awk '{print $1" is logged on"}'
root is logged on
root is logged on
[sam@chenwy sam]$ who
root :0 Nov 23 20:17
root pts/0 Nov 23 20:25 (:0.0)
a w k也允许传入环境变量。下面的例子使用环境变量HOME支持当前用户目录。可从pwd命令管道输出到a w k中获得相应信息。
代码:
[sam@chenwy sam]$ pwd | awk '{if ($1==derr) print $1}' derr=$HOME
/usr/sam
4. awk脚本文件
可以将a w k脚本写入一个文件再执行它。命令不必很长(尽管这是写入一个脚本文件的主要原因),甚至可以接受一行命令。这样可以保存a w k命令,以使不必每次使用时都需要重
关于tr
t r用来从标准输入中通过替换或删除操作进行字符转换。t r主要用于删除文件中控制字符或进行字符转换。使用t r时要转换两个字符串:字符串1用于查询,字符串2用于处理各种转换。
t r刚执行时,字符串1中的字符被映射到字符串2中的字符,然后转换操作开始
下面讲述:
引用:
& #8226; 大小写转换。
? 去除控制字符。
? 删除空行。
带有最常用选项的t r命令格式为:
代码:
t r - c - d - s [ " s t r i n g 1 _ t o _ t r a n s l a t e _ f r o m " ] [ " s t r i n g 2 _ t o _ t r a n s l a t e _ t o " ] i n p u t _f i l e
这里:
引用:
-c 用字符串1中字符集的补集替换此字符集,要求字符集为A S C I I。
-d 删除字符串1中所有输入字符。
-s 删除所有重复出现字符序列,只保留第一个;即将重复出现字符串压缩为一个字符串。
I n p u t - f i l e是转换文件名。虽然可以使用其他格式输入,但这种格式最常用。
字符范围
使用t r时,可以指定字符串列表或范围作为形成字符串的模式。这看起来很像正则表达式,但实际上不是。指定字符串1或字符串2的内容时,只能使用单字符或字符串范围或列表。
引用:
[a-z] a-z内的字符组成的字符串。
[A-Z] A-Z内的字符组成的字符串。
[0-9] 数字串。
/octal 一个三位的八进制数,对应有效的A S C I I字符。
[O*n] 表示字符O重复出现指定次数n。因此[ O * 2 ]匹配O O的字符串。
大部分t r变种支持字符类和速记控制字符。
字符类格式为[:c l a s s ],包含数字、希腊字母、空行、小写、大写、c n t r l键、空格、点记符、图形等等。
下表包括最常用的控制字符的速记方式及三位八进制引用方式。
当用一个单字符替换一个字符串或字符范围时,注意字符并不放在方括号里( [ ])。一些系统也可以使用方括号,例如可以写成[“ 0 1 2”]或“ 0 1 2”,t r也允许不加引号,因此命令中看到单引号而不是双引号时也不要感到奇怪。
像大多数系统工具一样, t r也受特定字符的影响。因此如果要匹配这些字符,需使用反斜线屏蔽其特殊含义。例如,用 {指定花括号左边可以屏蔽其特殊含义。
tr中特定控制字符的不同表达方式
代码:
速记符含义八进制方式
a Ctrl-G 铃声 0 0 7
b Ctrl-H 退格符 0 1 0
f Ctrl-L 走行换页 0 1 4
Ctrl-J 新行 0 1 2
r Ctrl-M 回车 0 1 5
Ctrl-I tab键 0 11
v Ctrl-X 0 3 0
去除重复出现的字符
下面文件包含了一些打印错误。这种情况时常发生,例如在v i编辑器中,偶尔按住一个键不放。
代码:
[sam@chenwy split]$ cat opps.txt
And the cowwwwws went homeeeeeeeeeeeee
Or did theyyyyyyyyyyyyy
如果要去除重复字母或将其压缩在一起,使用- s选项。因为都是字母,故使用[ a - z ]。输入文件重定向到t r命令。
代码:
[sam@chenwy split]$ tr -s "[a-z]"
所有重复字符被压缩成一个。如果使用c a t命令,再将结果管道输出至t r,结果是一样的。
代码:
[sam@chenwy split]$ cat opps.txt | tr -s "[a-z]"
And the cows went home
Or did they
删除空行
要删除空行,可将之剔出文件。下面是一个文件p l a n e . t x t。文本间有许多空行。
代码:
[sam@chenwy split]$ cat plane.txt
plane.txt
9879932 Spitfire
190992 Lancaster
238991 Typhoon
使用- s来做这项工作。换行的八进制表示为 0 1 2,命令为:
代码:
[sam@chenwy split]$ tr -s "[12]"
也可以使用换行速记方式 n。
代码:
[sam@chenwy split]$ tr -s "[ ]"
大写到小写
除了删除控制字符,转换大小写是t r最常用的功能。为此需指定即将转换的小写字符[ a - z ]和转换结果[ A - Z ]。
第一个例子,t r从一个包含大小写字母的字符串中接受输入。
代码:
[sam@chenwy split]$ echo "May Day,May Day,Going Down.." | tr "[a-z]" "[A-Z]"
MAY DAY,MAY DAY,GOING DOWN..
同样,也可以使用字符类[:l o w e r:]和[:u p p e r:]。
代码:
[sam@chenwy split]$ echo "May Day,May Day,Going Down.." | tr "[:lower:]" "[:upper:]"
MAY DAY,MAY DAY,GOING DOWN..
删除指定字符
偶尔会从下载文件中删除只包含字母或数字的列。需要结合使用- c和- s选项完成此功能。
下面的文件包含一个星期的日程表。任务是从其中删除所有数字,只保留日期。日期有大写,也有小写格式。因此需指定两个字符范围[ a - z ]和[ A - Z ],命令tr -cs "[a-z][A-Z]""[12*]"将文件每行所有不包含在[ a - z ]或[ A - Z ](所有希腊字母)的字符串放在字符串1中并转换为一新行。- s选项表明压缩所有新行, - c表明保留所有字母不动。原文件如下,后跟t r命令:
代码:
[sam@chenwy split]$ cat diary.txt
mondy 10:50
Tuesday 15:00
wednesday 15:30
thurday 10:30
Fridya 09:20
代码:
[sam@chenwy split]$ tr -cs "[a-z][A-Z]" "[12*]"
转换控制字符
t r的第一个功能就是转换控制字符,特别是从d o s向U N I X下载文件时,忘记设置f t p关于回车换行转换的选项时更是如此。
下面是故意没有设置转换开关的一个文本文件,是关于文具需求的一部分内容。使用c a t- v显示控制字符。
代码:
[sam@chenwy split]$ cat -v stat.tr
Boxes paper 12^M
Clips metal 50^M
Pencils-meduim 10^M
^Z
猜想‘中间空的是’是t a b键。每一行以C t r l - M结尾,文件结尾C t r l - Z,以下是改动方法。
使用- s选项,查看A S C I I表。^的八进制代码是1 3 6,^ M是0 1 5,t a b键是0 11,^ Z是0 3 2 ,下面将按步骤完成最终功能。
用新行替换每行末尾的^ M,并用 n去除^ Z,输入要来自于临时工作文件s t a t . t m p。将结果重定向到临时工作文件s t a t . t m p。
代码:
[sam@chenwy split]$ tr -s "[1532]" " " stam.tmp
[sam@chenwy split]$ cat -v stam.tmp
Boxes paper 12
Clips metal 50
Pencils-meduim 10
快速转换
如果需要删除文件中^ M,并代之以换行。使用命令:
代码:
[sam@chenwy split]$ tr -s "[15]" " "
或者用下述命令得同样结果。
代码:
[sam@chenwy split]$ tr -s "[15]" " " stat1.tr
[sam@chenwy split]$ cat stat1.tr
Boxes paper 12
Clips metal 50
Pencils-meduim 10
也可以用下述命令:
代码:
[sam@chenwy split]$ tr -s "[ ]" " "
代码:
[sam@chenwy split]$ tr -s " " " "
另一个一般的D o s到U N I X转换是命令:
代码:
[s