Linux shell编程
1.1 shell变量
为使shell
编程更有效,系统提供了一些shell
变量。变量可以定制用户本身的工作环境。使用变量可以保存有用信息,使系统获知用户相关设置;变量也用于保存暂时信息。
有两种变量:本地变量
和
环境变量。
(1
)本地变量
本地变量在用户现在的Shell
生命期的脚本中使用。
要设置一本地变量,格式为:
variable-name = value or ${variable-name=value}
定义变量:可以使用export
来定义导出一变量,export variable-name=”variable-value”
显示变量:使用echo
命令可以显示单个变量值,echo $variable-name
清除变量:使用unset
命令清除变量,unset variable-name
显示所有变量:使用set
显示所有本地定义的shell
变量;使用env
显示所有shell
变量。
例子:
$ export MYNAME=”hongdy”
$ echo $MYNAME
hongdy
$ env
$ set
$ unset MYNAME
(2
)环境变量
环境变量用于所有用户进程(
子进程)
,登录进程为父进程。Shell
中执行的用户进程均为子进程
。最好在.profile
中定义,系统在/etc/profile
文件中已经设置了一些环境变量。
设置环境变量:使用export
导出环境变量,export VARIABLE-NAME
显示环境变量:使用echo
命令可以显示环境变量,echo $(VARIABLE-NAME)
清除环境变量:使用unset
命令清除环境变量,unset VARIABLE-NAME
显示所有环境变量:env
显示所有环境变量
下面显示的是Shell
的一些基本环境变量
变量
|
说明
|
SHELL
|
变量保存缺省shell
,在/etc/passwd
中设置
|
TERM
|
保存终端类型
|
TZ
|
时区变量保存时区值
|
HOME
|
用于保存注册目录的完全路径名
|
UID
|
当前用户的标识符,取值是由数字构成的字符串
|
PWD
|
当前工作目录的绝对路径名,该变量的取值随
cd命令的使用而变化
|
PS1
|
主提示符,在
root用户缺省的主提示符是“#”,普通用户缺省是“$”
|
PS2
|
在
shell接收用户输入命令的过程中,如果用户在输入行的末尾输入“\”然后回车,或者当用户按回车键时shell判断出用户输入的命令没有结束时,显示这个辅助提示符,提示用户继续输入命令的其余部分,缺省的辅助提示符是“>”
|
(3
)位置变量
位置参数是一种在调用
shell程序的命令行中按照各自的位置决定的变量,是在程序名之后输入的参数。位置参数之间用空格分隔,shell取第一个位置参数替换程序文件中的$1,第二个替换$2,依次类推。$0是一个特殊的变量,它的内容是当前这个shell程序的文件名即脚本名。
每个访问参数前要加
&符号,第一个参数为0,表示预留保存实际脚本名字。
比如向脚本传送
I love you
$0 &1 &2 &3
脚本名字
I love you
例子:
$ vi findfile
#!/bin/sh
find / -name $1 -print
$ chmod a+x findfile
$ ./findfile /etc/passwd
特定变量参数
参数
|
含义
|
$#
|
传递到脚本的参数个数
|
$*
|
所有位置参数的内容
|
$$
|
脚本运行的当前进程
ID号
|
$!
|
后台运行的最后一进程的进程
ID号
|
$0
|
当前执行的进程名
|
$@
|
与
$#相同,但在使用时要加引号
|
$?
|
命令执行后返回的状态
一般为
0
|
1.2 shell输入和输出
在shell
脚本中,可以用几种不同的方式读入数据:可以使用标准输入―缺省为键盘,或者指定一个文件作为输入。对于输出也是一样:如果不指定某个文件作为输出,标准输出总是和终端屏幕相关联。
(1
)echo
使用
echo命令可以显示文本或者把字符串输入到文件。
$ echo string
\c 不换行;\t 跳格;\n 换行;
-n选项禁止echo命令输出后换行,-e使转义符生效。
(2
)read
Read
语句从键盘或文件的某一行文本中读入信息,并将其付给一个变量,空格作为分隔符。
(3
)cat
cat
是一个简单而通用的命令,可以用它来显示文件内容,创建文件,还可以用它来显示控制字符。
例子:
$ export NAME=hongdy
$ echo $NAME
hongdy
在使用cat
命令时要注意,它不会在文件分页符处停下来;它会一下显示完整个文件。如果希望每次显示一页,可以使用more
命令或把cat
命令的输出通过管道传递到另外一个具有分页功能的命令中,比如:$ cat myfile | more
如果希望创建一个新文件,并向其中输入内容,cat
命令把标准输出重定向到该文件中,标准输入是键盘,输入完毕按ctrl+D
结束输入。
cat > myfile
hongdy
//
输入的内容,然后回车
ctrl+d
//
结束输入返回
(4
)pipe
可以通过管道把一个命令的输出传递给另一个命令作为输入,管道用竖杠
|
表示。
比如:$ ls | grep *.txt
在ls
结果中搜索txt
文件
(5
)tee
tee
命令作用可以用字母T
来形象地表示。它把输出的一个副本输送到标准输出,另一个副本拷贝到相应的文件中。
tee
命令的一般形式:tee �Ca files
-a
表示追加到文件末尾
比如:$ ls | tee result
将ls
结果显示到标准输出,同时拷贝到文件中。
(6
)重定向
标准输入是文件描述符0
;标准输出是文件描述符1
;标准错误是文件描述符2
$ command 1>filename // 把标准输出重定向到一个文件中
$ command > filename 2>&1 // 把标准输入和标准错误一起重定向到一个文件中
$ command < filename
//
命令以filename
文件作为标准输入
例子:
$ grep “hello” * > result
2>&1
gerp
的结果将标准错误重定向到标准输出。
1.3 后台执行命令
使某些进程在后台运行,也就是说不在终端屏幕上运行。后台执行命令有以下四种:
命令
|
含义
|
cron
|
系统调度进程,使用它在每天的非高峰负荷时间段运行作业,或在一周或一月中的不同时段运行
|
at
|
at
命令,在一个特定的时间运行一些特殊的作业,或在非负荷高峰时间段或高峰负荷时间段运行
|
&
|
使用它在后台运行一个占用时间不长的进程
|
nohup
|
使用它在后台运行一个命令,即使在用户退出时也不受影响
|
(1
)cron
cron
是一个linux
下的定时执行工具,可以在无需人工干预的情况下运行作业。由于Cron
是Linux
的内置服务,但它不自动启动,可以用以下的方法启动、关闭这个服务:
# /sbin/service crond
start
//
启动服务
# /sbin/service crond
stop
//
关闭服务
# /sbin/service crond
restart
//
重启服务
# /sbin/service crond
reload
//
重新载入配置
也可以将这个服务在系统启动的时候自动启动。
比如:在/etc/rc.d/rc.local
这个脚本的末尾加上
/sbin/service crond start
crontab
命令
crontab
命令的一般形式为:crontab [-u user] -e -l �Cr
-u
用户名;-e
编辑crontab
文件;-l
列出crontab
文件中的内容;-r
删除crontab
文件。
cron
服务提供crontab
命令来设定cron
服务的,以下是这个命令的一些参数与说明:
# crontab -u
//
设定某个用户的cron
服务,一般root
用户在执行此命令的时候需要此参数
# crontab -l
//
列出某个用户cron
服务的详细内容
# crontab -r
//
删除某个用户的cron
服务
# crontab -e
//
编辑某个用户的cron
服务
例子:
root
查看自己的cron
设置:# crontab -u root -l
root
删除hongdy
用户的cron
设置:
# crontab -u hongdy -r
在编辑cron
服务时,编辑的内容有一些格式和约定,输入:crontab -u root �Ce
,进入vi
编辑模式,编辑的内容一定要符合下面的格式:*/1 * * * * ls >> /tmp/ls.txt
这个格式的前一部分是对时间的设定,后面一部分是要执行的命令,如果要执行的命令太多,可以把这些命令写到一个脚本文件,然后在这里直接调用这个脚本文件,调用的时候写出命令的完整路径。
时间的设定我们有一定的约定,前面五个*
号代表五个数字,数字的取值范围和含义如下:
分钟(0-59)
;小�r(0-23)
;日期(1-31)
;月份(1-12)
;星期(0-6)//0
代表星期天
除了数字还有几个特殊的符号就是"*"
、"/"
和"-"
、","
,*
代表所有的取值范围内的数字,/
代表每的意思,"*/5"
表示每5
个单位,"-"
代表从某个数字到某个数字, ","
分开几个离散的数字。
例子
:
(1)
每天早上6
点
0 6 * * * echo "Good morning." >> /tmp/test.txt
(2)
每两个小时
0 */2 * * * echo "Have a rest" >> /tmp/test1.txt
(3)
晚上11
点到早上8
点之间每两个小时,早上八点
0 23-7/2
,8 * * * echo "Have a good dream" >> /tmp/test3.txt
(4)
每个月的4
号和每个礼拜的礼拜一到礼拜三的早上11
点
0 11 4 * 1-3 echo "Have a rest" >> /tmp/test4.txt
(5)
1
月1
日
早上4
点
0 4 1 1 * echo "Have a rest" >> /tmp/test5.txt
每次编辑完某个用户的cron
设置后,cron
自动在/var/spool/cron
下生成一个与此用户同名的文件,此用户的cron
信息都记录在这个文件中,这个文件是不可以直接编辑的,只可以用crontab -e
来编辑。cron
启动后每过一份钟读一次这个文件,检查是否要执行里面的命令。因此此文件修改后不需要重新启动cron
服务。
(2
)at
命令
at
命令允许用户向cron
守护进程提交作业,使其在稍后的时间运行。这里稍后的时间可能是指10 min
以后,也可能是指几天以后。如果你希望在一个月或更长的时间以后运行,最好还是使用crontab
文件。一旦一个作业被提交,a t
命令将会保留所有当前的环境变量。
At
命令
at
命令的基本形式为:
at [-f script] [-m -l -r] [time] [date]
其中:
-f
script
是所要提交的脚本或命令。
-m
作业完成后给用户发邮件。
-l
列出当前所有等待运行的作业。
-r
清除作业。为了清除某个作业,还要提供相应的作业标识(ID)
。
time at
命令的时间格式非常灵活;可以是H
、HH . HHMM
、HH:MM
或H:M
,其中H
和M
分别是小时和分钟。还可以使用a . m .
或p . m .
。
date
日期格式可以是月份数或日期数,而且at
命令还能够识别诸如today
之类的词。
使用at
命令提交作业有几种不同的形式:可以通过命令行方式,也可以使用a t
命令提示符。
如果你想提交若干行的命令,可以在at
命令后面跟上日期/
时间并回车。然后就进入了at
命令提示符,这时只需逐条输入相应的命令,然后按 <CTRL-D>
退出。
例子:
提交作业
# at 10:50
at> find / -name “passwd” �Cprint
at> <EOT>
其中<EOT>
就是<CTRL-D>
。
清除作业
$atrm [job no]
或 at -r [job no]
要清除某个作业,首先要执行at -l
命令,以获取相应的作业标识,然后对该作业标识使用at -r
命令,清除该作业。
(3
)&
命令
当在前台运行某个作业时,终端被该作业占据;而在后台运行作业时,它不会占据终端。
可以使用&
命令把作业放到后台执行。
(4
)nohup
命令
如果正在运行一个进程,而且觉得在退出帐户时该进程还不会结束,那么可以使用nohup
命令。该命令可以在你退出帐户之后继续运行相应的进程。Nohup
就是不挂起的意思( no hang up)
。
nohup
命令的一般形式为:nohup command &
1.4 正则表达式
使用shell
时,从一个文件中抽取多于一个字符串将会很麻烦。例如:在一个文本中抽取一个词,它的头两个字符是大写的,后面紧跟四个数字。如果不使用某种正则表达式,在shell
中将不能实现这个操作。
基本元字符集及其含义
元字符
|
含义
|
^
|
只匹配行首
|
$
|
只匹配行尾
|
*
|
一个单字符后紧跟
*,匹配0个或多个此单字符
|
[]
|
匹配
[]内字符。可以是一个单字符,也可以是字符序列。可以使用-表示[]内字符序列范围,如用[1-5 ]代替[12345]
|
\
|
用来屏蔽一个元字符的特殊含义。因为
shell中一些元字符有特殊含义。\可以使其失去应有意义匹配任意单字符
|
pattern\{n\}
|
只用来匹配前面
pattern出现次数,n为次数
|
pattern\{n,\}m
|
含义同上,但次数最少为
n
|
pattern\{n,m\}
|
含义同上,但
pattern出现次数在n与m之间
|
使用句点匹配单字符
句点“.
”可以匹配任意单字符。例如:如果要匹配一个字符串,以beg
开头,中间夹一个任意字符,那么可以表示为beg.n
,“.
”可以匹配字符串头,也可以是中间任意字符。
在行首以^
匹配字符串或字符序列
^
只允许在一行的开始匹配字符或单词。
在行尾以$
匹配字符串或字符
可以说$
与^
正相反,它在行尾匹配字符串或字符, $
符号放在匹配单词后。假定要匹配以单词txt
结尾的所有行txt$
使用*
匹配字符串中的单字符或其重复序列
使用此特殊字符匹配任意字符或字符串的重复多次表达式。例如:compu*t
将匹配字符u
一次或多次。
使用\
屏蔽一个特殊字符的含义
有时需要查找一些字符或字符串,而它们包含了系统指定为特殊字符的一个字符。
什么是特殊字符?一般意义上讲,下列字符可以认为是特殊字符: $ . ‘ “” [ ] ^ | () \ + ?
假定要匹配包含字符“.
”的各行而“.
”代表匹配任意单字符的特殊字符,因此需要屏蔽其含义。操作如下:\ .
不认为反斜杠后面的字符是特殊字符,而是一个普通字符即句点。
如果要在正则表达式中匹配以*.txt
结尾的所有文件,可做如下操作:
\*\.txt
即可屏蔽字符*
的特定含义。
使用\{\}
匹配模式结果出现的次数
使用*
可匹配所有匹配结果任意次,但如果只要指定次数,就应使用\ { \ }
,此模式有三种形式,即:
pattern\{n\}
匹配模式出现n
次。
pattern\{n,\}
匹配模式出现最少n
次。
pattern\{n,m}
匹配模式出现n
到m
次之间,n , m
为0 - 2 5 5
中任意整数。
1.5 grep
全局正则表达式版本grep
允许对文本文件进行模式查找,可以和正则表达式一起使用。
grep
一般格式:grep [
选项]
基本正则表达式
[
文件]
基本正则表达式可为字符串。在grep
命令中输入字符串参数时,最好将其用双引号括起来。有两个原因,一是以防被误解为shell
命令,二是可以用来查找多个单词组成的字符串。
常用的grep
选项:
选项
|
含义
|
-c
|
只输出匹配行的计数
|
-i
|
不区分大小写
|
-h
|
查询多文件时不显示文件名
|
-l
|
查询多文件时只输出包含匹配字符的文件名
|
-n
|
显示匹配行及行号
|
-s
|
不显示不存在或无匹配文本的错误信息
|
-v
|
显示不包含匹配文本的所有行
|
-R
|
递归进入子目录
|
缺省情况下, grep
是大小写敏感的,如要查询大小写不敏感字符串,必须使用-i
开关。
精确匹配:每个匹配模式中抽取字符串后有一个<Tab>
键;使用grep
抽取精确匹配的一种更有效方式是在抽取字符串后加\>
。
使用正则表达式使模式匹配加入一些规则,因此可以在抽取信息中加入更多选择。使用正则表达式时最好用单引号括起来,这样可以防止grep
中使用的专有模式与一些shell
命令的特殊方式相混淆。
例子:
# grep �CR �Cn �Cs “hongdy” /etc
查找/etc
目录下所有包含hongdy
的文件。-R
表示递归查找etc
下的所有目录;-n
表示显示匹配行及行号;-s
表示不显示不存在或无匹配文件的错误信息。
1.6 tr
tr
用来从标准输入中通过替换或删除操作进行字符转换,主要用于删除文件中控制字符或进行字符转换。
使用tr
时要转换两个字符串:字符串1
用于查询,字符串2
用于处理各种转换。tr
刚执行时,字符串1
中的字符被映射到字符串2
中的字符,然后转换操作开始。
tr
命令的一般格式:tr �Cc �Cd �Cs [ “string_to_translate_from” ] [ “string2_translate_to ] file
-c
用字符串1
中字符集的补集替换此字符集
-d
删除字符串1
中所有的输入字符
-s
输出有重复出现的字符序列,只保留第一个。
(1
)删除重复出现的字符
$ tr �Cs “[a-z]” < file
去除文件file
中所有重复字母
(2
)删除空行
使用-s
,换行的八进制表示为\012
或使用换行速记方式 \n
。
$ tr �Cs “[\012]” < test.txt
//
使用八进制方式
$ tr �Cs [“\n”]
< test.txt
//
使用转义字符
(3
)大小写字母转换
$ echo “ CHINA ” | tr “[A-Z]” “[a-z]”
//
转换成小写字母
$ echo “china”
| tr “[a-z] “ “[A-Z]”
//
转换成大写字母
$ cat file1 | tr “[A-Z]” “[a-z]” > file2
//
将文件file1
大写转换成小写并输出至文件file2
(4
)删除指定字符
结合使用- c
和- s
选项。
$ tr �Ccs “[A-Z][a-z]” “[\012*]” < file
//
删除文件中所有数字,只保留字母。
(5
)转换控制字符
tr
的第一个功能就是转换控制字符,特别是从dos
向UNIX
转换。
使用 cat �Cv
显示控制字符。
$ cat �Cv test.txt
Zhou ^^^^^^12^M
Xu^^^^^^50^M
Hong^^^^100^M
^Z
‘^^^^^^’
是tab
键。每一行以Ctrl-M
结尾,文件结尾Ctrl-Z
。
查看ASCII
表,^
的八进制代码是136
,^M
是015
,tab
键是01
,^Z
是032
$ tr �Cs “[\136]” “[\011*]”
< test.txt
$ tr �Cs “[\015\032]” [“\n”] < test.txt
1.7 sed
1.7.1 sed概述
sed
是一种在线编辑器,它一次处理一行内容。处理时把当前处理的行存储在临时缓冲区中,称为模式空间(pattern space
),接着用sed
命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。Sed
主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。
可以通过定址来定位你所希望编辑的行,该地址用数字构成,用逗号分隔的两个行数表示以这两行为起止的行的范围,如1
,3
表示1
、2
、3
行,美元符号$
表示最后一行。范围可以通过数据,正则表达式或者二者结合的方式确定
。在sed
语句,正则表达式必须封闭在//
中间。在sed
地址管理中默认是对全局进行操作,地址可以分为行地址和模式地址。如1,10d 12d 10,$d
表示行地址,如/^$/d
表示模式地址。用户在进行操作时,可以对行地址和模式地址结合进行操作。
1.7.2 Sed命令
调用sed
命令有两种形式:
sed [options] 'command' file(s)
sed [options] -f scriptfile file(s)
选项
-e command, --expression=command
允许多台编辑。
-h, --help
打印帮助,并显示bug
列表的地址。
-n, --quiet, --silent
取消默认输出。
-f, --filer=script-fil
引导sed
脚本文件名。
-V, --version
打印版本和版权信息。
(1
)替换命令
语法格式:sed ‘s/old/new/flags’ file
文件file
中内容old
用new
替换。
Flags
:g
表示对全局进行替换;w file
:表示匹配行的内容写到另一个文件中。
例如:
$ sed ‘s/unix/Linux/g’ file
文件file
中所有内容unix
都用Linux
替换
$ sed ‘s/unix/Linux/w 11’ file
文件file
中所有内容unix
都用Linux
替换并将结果写入11
(2
)删除命令
语法格式:sed ‘[address]d’ file
删除文件file
中address
表达的内容
例如:
$ sed ‘/^\.aa/d’ file
删除文件file
中以.aa
开头的内容
$ sed ‘/^$/d’ file
删除文件file
中空行的内容
$ sed ‘10d’ file
删除文件file
中第10
行的内容。
(3
)追加命令
语法格式:sed ‘[address]a\text’ file
文件file
中address
表达的行后面添加text
内容
例如:
$ sed ' 10a \abcd' file
在文件file
中的第10
行后面追加一行abcd
字符
$ sed '/unix/a\abcd' file
在文件file
中所有出现unix
字符的行后面追加一行abcd
字符
$ sed '/unix/a\abcd\n\dcba' file
在文件file
中出现unix
字符行后面追加两行字符:abcd
和dcba
。
(4
)插入命令
语法格式:sed ‘[address]i\text’ file
文件file
中address
表达内容行前面插入text
内容。
例如:$ sed '/unix/i\abcd´ file
在文件file
中unix
行前面插入abcd
字符
(5
)更改命令
语法格式:sed ‘[address]c\text’ file
文件file
中address
表达内容的行用text
代替。
例如:$ sed ' 1c \aaaa' file
将文件file
中第一行的内容更改为aaaa.
(6
)打印行号
语法格式:sed ‘[address]=’ file
打印
文件file
中address
表达内容的行号
例如:
$ sed '/unix/=' file
打印文件file
中包含unix
字符的行号。不包含unix
字符的行不打印行号。
(7
)读写文件
语法格式:sed ‘[address]r file 1’ file
文件file
中address
表达的内容后插入文件file1
的内容
语法格式:sed ‘[address]w file1’ file
文件file
中address
表达的内容写入文件file2
中
例如:
$ sed '/unix/r 11’ file
在文件file
中unix
字符的后面读入11
文件的内容
$ sed '2r 11' file
在文件file
的第2
行后面读入11
文件的内容
$ sed '2,5w 22' file
将文件file
中第2
行到第5
行的内容写到22
文件中去。
1.8 awk
1.8.1 awk概述
awk
是一种样式扫描与处理工具,与sed
和grep
很相似,但其功能却大大强于sed
和grep
,它几乎可以完成grep
和sed
所能完成的全部工作,同时它还可以可以进行样式装入、流控制、数学运算符、进程控制语句甚至于内置的变量和函数。
awk
是以三个创立者的名字(Aho
、Peter Weinberg
和Brain Kernighan
)的缩写命名的,awk
拥有自己的语言:awk
程序设计语言。awk
语言的最基本功能是在文件或字符串中基于指定规则浏览和抽取信息。awk
抽取信息后才能进行其他文本操作。
1.8.2 Awk调用
有三种方式可以调用awk
(1
)
awk
命令行:awk [-F field-separator] ‘commands’ input-file(s)
[-F
域分隔符]
是可选的,awk
使用空格作为缺省的分隔符;commands
是真正的awk
命令;input-files
是一个或多个输入文件。
(2
)将所有的awk
命令插入一个脚本文件并以 #!/bin/awk -f
作为首行,给予该脚本可执行权限,然后在shell
下通过键入该脚本的脚本名调用之。
(3
)
将所有的awk
命令插入一个单独脚本文件,然后使用: awk -f awk-script-file input_file(s)
-f
选项指明文件awk-script-file
中的awk
脚本,input-files
是一个或多个输入文件。
awk
命令的一般形式
awk ' BEGIN { actions }
awk_pattern1 { actions }
............
awk_patternN { actions }
awk END { actions }
其中 BEGIN { actions }
和 END { actions }
是可选的。
awk
脚本文件
#!/bin/awk -f
BEGIN { print "this is the begin of awk script" }
{ print $1, $2, $3 }
END { print "this is the end of awk script" }
第一行是!/bin/awk -f
。这很重要,没有它自包含脚本将不能执行。这一行告之脚本系统中awk
的位置。通过将命令分开,脚本可读性提高,还可以在命令之间加入注释,使用“#”
作为注释符,它使“#”
到行尾的内容成为注释。
域和记录
awk
执行时,其浏览域标记为$1
、$2 . . . $n
,这种方法称为域标识。使用这些域标识将更容易对域进行进一步处理。使用$ 1 , $ 3
表示参照第1
和第3
域,注意这里用逗号做域分隔。如果希望打印一个有5
个域的记录的所有域,不必指明$ 1 , $ 2 , $ 3 , $ 4 , $ 5
,可使用$ 0
,意即所有域。
例子:
$ awk -F ":" '{print $1, $3, $6}' /etc/passwd
打印passwd
文件的用户名、用户ID
和用户目录
$ awk '/hongdy/{print}' /etc/passwd
显示文本文件passwd
含有字符串hongdy
的所有行。
流程控制结构
(1
)if (condition) {then-body} [else {else-body}]
(2
)while (condition) {body}
(3
)do {body} while (condition)
(4
)for (initialization; condition; increment) {body}
其中condition
一般为布尔表达式,body
和else-body
是awk
语句块。
1.8.3 awk变量
在awk
脚本中的表达式中要经常使用变量。不要给变量加双引号,因为awk
将视之为字符串。awk
的变量基本可以分为两类:awk
内部变量和自动以变量。
1.8.3.1 awk内部变量
awk
的内部变量用于存储awk
运行时的各种参数,这些内部变量又可以分为:
(1
)自动内部变量:这些变量的值会随着awk
程序的运行而动态的变化,在awk_script
中改变这些变量的值是没有意义的(
即不应该被赋值)
。
常用的自动内部变量
变量
|
说明
|
NF
|
当前输入字段的字段数
|
NR
|
当前输入文件中已经被awk
读取过的记录行的数目
|
FNR
|
已经被awk
读取过的记录行的总数目。只有一个输入文件时FNR
和NR
相等
|
ARGC
|
命令行参数个数
|
FILENAME
|
当前输入文件的文件名
|
ARGIND
|
当前被处理的文件在数组ARGV
内的索引
|
|
|
例子:
$ awk ‘{ print “filename=” FILENAME,
“count=” NF}’ 11
假设文件11
内容为123 abc
。
Filename=11, count=2
(2
)字段变量($0 $1 $2 $3 ...)
当awk
把当前输入记录分段时,会对这些字段变量赋值。在awk
运行过程中字段变量的值是动态变化的。可以创建新的输出字段,比如当前输入记录被分割为8
个字段,这时可以通过对变量 $9 (
或$9
之后的字段变量)
赋值而增加输出字段,NR
的值也将随之变化。
字段变量支持变量名替换。
例子:
$ pwd | awk -F/
'{print $NF}'
打印输入记录的最后一个字段
如果当前目录为/home/hongdy/linux , awd
以/
为分隔符,有三个字段,输入的最后一个字段为linux
$ awk '{x=2;print $x}' 11
打印输入记录的第2
个字段
如果文件11
的内容为zhou xu hong
,
有三个字段,第二个字段即为xu
(3
)其它内部变量
可以修改这些变量。常见的有:
变量
|
说明
|
FS
|
输入记录的字段分隔符(
默认是空格和制表符)
|
OFS
|
输出记录的字段分隔符(
默认是空格)
|
OFMT
|
数字的输出格式(
默认是 %. 6g )
|
RS
|
输入记录间的分隔符(
默认是NEWLINE)
|
ORS
|
输出记录间的分隔符(
默认是NEWLINE)
|
ARGV
|
命令行参数数组
|
ENVIRON
|
存储系统当前环境变量值的数组
|
|
|
例如:
$ cat /etc/passwd | awk 'BEGIN { FS=":" } {print "User name: "$1,"UID: "$3}'
文件/etc/passwd
内容类似于 hongdy:x:501:501:hongdy:/home/hongdy:/bin/sh
,以:为分隔符,打印用户名和用户ID
。
1.8.3.2 自定义变量
(1
)定义变量varname=value (
自定义变量不需声明,赋值语句同时完成变量定义和初始化)
(2
)表达式中出现不带双引号的字符串都被视为变量,如果未赋值默认值为0
或空字符串。
1.8.4 Awk函数
可以在awk_script
的任何地方使用awk
函数。awk
函数可以分为内置函数和自定义函数。
1.8.4.1 awk内置函数
(1
)常见awk
内置数值函数
函数名
|
说明
|
int(x)
|
求出x
的整数部份,朝向0
的方向做舍去。eg: int(3.9)
是3
|
sqrt(x)
|
求出x
正的平方根值。eg: sqrt(4)=2
|
exp(x)
|
求出x
的次方。eg: exp(2)
即是求e*e
。
|
log(x)
|
求出x
的自然对数。
|
sin(x)
|
求出x
的sine
值,x
是弪度量。
|
cos(x)
|
求出x
的cosine
值,x
是弪度量。
|
rand ()
|
得到一个随机数(
平均分布在0
和1
之间)
。
|
srand(x)
|
设定产生随机数的seed
为x
。
|
|
|
例子:$ awk '{ print log($1)}' 12
假设文件12
的内容第一个域值是2
,打印log2
的值
(2
)常见awk
内置字符串函数
函数名
|
说明
|
index(in, find)
|
返回字符串in
中字符串find
第一次出现的位置(
索引从1
开始)
,如果在字串in
中找不到字符串find
,则返回值为0
。
|
length(s)
|
求出字符串s
的字符个数。eg: length("abcde")
是5
。
|
match(s,r)
|
r
在字符串s
的第一次出现的位置,如果s
不包含r
,则返回值0
。
|
sprintf(fmt,exp1,...)
|
返回模式字符串:
和printf
类似印出
|
sub(p, r,t)
|
在字符串t
中寻找符合模式字符串p
的最靠前最长的位置,并以字符串r
代替最前的p
。
|
substr(str, st, len)
|
传回str
的子字符串,其长度为len
字符,从str
的第st
个位置开始。如果len
没有出现,则传回的子字符串是从第st
个位置开始至结束。
|
split(s,a,fs)
|
在分隔符fs
为分隔符将字符串s
分隔成一个awk
数组a
,并返回a
的下标数
|
tolower(str)
|
将字符串str
的大写字母改为小写字母
|
toupper(str)
|
将字符串string
的小写字母改为大写字母
|
例子:$ echo "abcdefg" | awk '{print length($0)}'
打印字符串“abcdefg”
的长度
(3
)常见awk
内置系统函数
函数名
|
说明
|
close(filename)
|
将输入或输出的文件filename
关闭
|
system(command)
|
此函数允许调用操作系统的指令,执行完后将回到awk
程序
|
1.8.4.2 自定义函数
复杂的awk
常常可以使用自己定义的函数来简化。调用自定义的函数与调用内置函数的方法一样。
自定义函数定义的格式:自定义函数可以在awk
程序的任何地方定义。
function fun_name (parameter_list) { // parameter_list
是以逗号分隔的参数列表
body-of-function //
函数体,是awk
语句块
}
例子:
$ awk '{ print "sum =" SquareSum($1,$2) } function SquareSum(x,y) { sum=x*x+y*y ; return sum } ' 11
自定义了一个平方和的函数,从文件11
中取出两个数,打印他们的平方和。
或者写到脚本文件里
$ vi awk_fun
#!/bin/awk -f
function SquareSum(x, y){ sum = x*x + y*y; return sum;}
{print "sum=", SquareSum($1, $2)}
$ chmod a+x awk_fun
$ ./awk_fun 11
sum=13
假设文件11
的内容是2 3
,平方和是13
。
1.8.4.3 Awk输出函数printf
awk
提供函数printf
,拥有几种不同的格式化输出功能,每一种printf
函数都以一个%
符号开始,以一个决定转换的字符结束。printf
函数基本语法是printf([
格式控制符]
,参数)。
例子:
$ awk '{ printf("%d
%s\n", $1, $2) }' 11
123
abc
假设文件11
的内容是数字123
和字符串abc
,使用printf
打印整型数和字符串。
1.9 引号
符号
|
含义
|
双引号
|
可以引用除字符$
、\
外的任意字符或字符串
|
单引号
|
与双引号类似,不同的是shell
会忽略任何引用值
|
反引号
|
设置系统命令的输出到变量,shell
将反引号中的内容作为一个系统命令并执行
|
反斜线
|
屏蔽特殊含义
& * + $ ` “ | ?
|
1.10 命令执行顺序
在执行某个命令的时候,有时需要依赖于前一个命令是否执行成功。比如:往某个文件夹写入文件,要先判断那个文件夹是否存在,否则无法写入文件。
如果希望在成功地执行一个命令之后再执行另一个命令,或者在一个命令失败后再执行另一个命令,&&
和
| |
可以完成这样的功能。相应的命令可以是系统命令或shell
脚本。Shell
还提供了在当前shell
或子shell
中执行一组命令的方法,即使用()和{ }
。
(1
)使用 &&
&&
左边的命令返回真(即返回0
,成功被执行)后,&&
右边的命令才能够被执行。
(2
)使用 ||
||
左边的命令未执行成功,||
右边的命令才能够执行。
(3
)用 ()
和 {}
将命令结合在一起
为了在shell
中执行一组命令,用命令分隔符隔开命令,并把所有的命令用()
或{}
括起来。
()
的一般形式:
(
命令1;
命令2;….)
{}
的一般形式:
{
命令1;
命令2; . . }
如果使用{}
代替()
,那么相应的命令将在子shell
而不是当前shell
中作为一个整体被执行。
例子:
$ [ -d test ] && rmkdir test
//
如果当前目录下的test
是个目录,则删除这个目录
$ [ -f test ] || touch test
//
如果test
不是一个正规文件,则创建一个文件
1.11 控制流结构
(1
)if
语句
if then else
语句的一般格式:
if
条件1
then
命令1
elif
条件2
then
命令2
else
命令3
fi
使用
if语句时,必须将then部分放在新行,否则会产生错误。如果不分行,必须使用命令分隔符。
例子:
vi if1.sh
#!/bin/sh
echo "Please input your name?"
read NAME
echo "Please input your sex?"
read SEX
if [ $SEX = "male" ] ; then
echo "Name: $NAME
Sex: male"
elif [ $SEX = "female" ]; then
echo "Name: $NAME
Sex: female"
else
echo "Name: $NAME
Sex: unkonwn"
fi
程序提示用户输入姓名和性别,然后判断性别并打印结果。
(2
)case
语句
case
语句的一般格式:
case
值 in
模式1}
命令1
…
;;
模式2}
命令2
…
;;
esac
例子:
$ vi case1.sh
#!/bin/sh
echo -n "Enter a number from 1 to 3:"
read NUM
case $NUM in
1)
echo "your select 1"
;;
2)
echo "your select 2"
;;
3)
echo "your select 3"
;;
*)
echo "your select out 1-3"
;;
esac
程序提示用户输入选择,然后打印用户的选择。
(3
)for
循环
For循环的一般格式:
for
变量名 in
列表
do
命令1
命令2
done
for循环不使用in列表选项,接受命令行位置参数作为参数,查看特定参数$@ 或 $*,已从命令行中取得参数
for param in "$@" or for param in "$*"
例子:
vi for1.sh
for loop in 1 2 3 4 5
do
echo $loop
done
(4
)while
循环
While循环用于不断执行一系列命令,也用于从文件中读取数据。
While循环的一般格式:
while
命令
do
命令1
命令2
...
done
例子:
$ vi while1.sh
#!/bin/sh
COUNTER=0
while [ $COUNTER -lt 5 ]
do
COUNTER=`expr $COUNTER + 1`
echo $COUNTER
done
COUNTER
初始化为0
,然后每次加1
并打印,直到COUNTER
小于5
。
1.12 条件测试
1.12.1 测试文件状态
test一般有两种格式:test condition 或 [condition]
文件状态有:
(
1)-d目录;(2)-s非空;(3)-f正规文件;(4)-L符号连接;
(
5)-r可读;(6)-w可写;(7)-x可执行
例子:
$ ls �Cal
test.file
-rw-r-r--
test.file
$ test -w test.file
或 [ -w test.file ]
$ echo $?
测试时可使用逻辑操作符
(
1)-a逻辑与:
操作符两边均为真,结果为真,否则为假。
(
2)-o逻辑或:
操作符两边一边为真,结果为真,否则为假。
例子:
$ [ -w file1 -a �Cw file2 ]
$ echo $?
1.12.2 字符串测试
字符串测试的一般格式:
[ string1 string_operation string2 ]
字符串操作符有:
(
1)= 两个字符串相等; (2)!= 两个字符串不相等
(
3)-z 空串 (4)-n 非空串
例子:
$ [ -z $EDITOR ] 测试环境变量EDITOR是否为空
$ echo $?
1.12.3 数值测试
数值测试的一般格式:
["number" numeric_operator "number" ]
数值操作符有:
(
1)-eq数值相等; (2)-ne数值不相等;
(
3)-gt第一个数大于第二个数; (4)-lt第一个数小于第二个数;
(
5)-le第一个数小于等于第二个数; (6)-ge第一个数大于等于第二个数
例子:
$ [ 31 -eq 31 ]
$ echo $?
expr
用法
expr
命令一般用于整数值,但也可用于字符串。
expr
的一般格式:expr argument operator argument
例子:
$ expr 10 + 10
数值前后要有空格
$ expr 30 / 3
$ expr 20 \* 3
//
使用乘号时必须用反斜线屏蔽其特定含义
expr
在循环中用于增量计算。首先循环初始化为0
,然后循环值加1
,反引号的用法意即替代命令。
最基本的一种是从(expr)
命令接受输出并将之放入循环变量。
例子:
$ LOOP = 0
等于号前后不能有空格
$ LOOP = `expr $ LOOP + 1`
加号前后要有空格
$ echo $LOOP
打印LOOP
值
1
1.13 shell函数
函数由两部分组成:
函数标题
和
函数体。
标题是函数名,标题名应该唯一;函数体是函数内的命令集合。
定义函数的格式为:
定义函数也可在函数名前加
function。
所有函数在使用前必须定义,一般将函数放在脚本开始部分。调用函数仅使用其函数名即可。
例子:
vi function1.sh
my_date ()
{
echo “Today’s date is `date` “
}
echo “now execute function my_date”
my_date
$ sh function1.sh
Now execute function hello
Today's date is Wed Feb 27 22:48:21 CST 2008
定义一个
my_date函数,主要功能是打印当前的日期时间,然后执行这个函数。
定位
shell函数:. function.sh
使用
shell函数:function_name
删除
shell函数:unset function_name
例如:
$ . function1.sh 定位shell函数
$ set | grep my_date 在set环境变量里查看函数my_date是否被载入
my_date()
$ my_date 使用shell函数
Today's date is Wed Feb 27 22:48:21 CST 2008
$ unset my_date 删除shell函数
$ set | grep my_date 在set环境变量里查看函数my_date是否存在
向函数传递参数
向函数传递参数就像在一般脚本中使用特殊变量$ 1 , $ 2 . . . $ 9
一样,函数取得所传参数后,将原始参数传回shell
脚本,因此最好先在函数内重新设置变量保存所传的参数。函数调用参数的转换以下划线开始,后加变量名,如_name.
从调用函数中返回
当函数完成处理或希望函数基于某一测试语句返回时,可做两种处理:
1)
让函数正常执行到函数末尾,然后返回脚本中调用函数的控制部分。
2)
使用return
返回脚本中函数调用的下一条语句,可以带返回值,0
为无错误,1
为有错误。
可以直接在脚本调用函数语句的后面使用最后状态命令($?
)来测试函数的返回值。