Linux Shell 常用技巧(转)
一. 特殊文件: /dev/null和/dev/tty
Linux系统提供了两个对Shell编程非常有用的特殊文件,/dev/null和/dev/tty。其中/dev/null将会丢掉所有写入它的数据,换句换说,当程序将数据写入到此文件时,会认为它已经成功完成写入数据的操作,但实际上什么事都没有做。如果你需要的是命令的退出状态,而非它的输出,此功能会非常有用,见如下Shell代码:
/> vi test_dev_null.sh
#!/bin/bash
if grep hello TestFile > /dev/null
then
echo "Found"
else
echo "NOT Found"
fi
在vi中保存并退出后执行以下命令:
/> chmod +x test_dev_null.sh #使该文件成为可执行文件
/> cat > TestFile
hello my friend
CTRL +
D
#退出命令行文件编辑状态
/> ./test_dev_null.sh
Found
#这里并没有输出grep命令的执行结果。
将以上Shell脚本做如下修改:
/> vi test_dev_null.sh
#!/bin/bash
if grep hello TestFile
then
echo "Found"
else
echo "NOT Found"
fi
在vi中保存退出后,再次执行该脚本:
/> ./test_dev_null.sh
hello my
friend
#grep命令的执行结果被输出了。
Found
下面我们再来看/dev/tty的用途。当程序打开此文件时,Linux会自动将它重定向到一个终端窗口,因此该文件对于读取人工输入时特别有用。见如下Shell代码:
/> vi test_dev_tty.sh
#!/bin/bash
printf "Enter new password: " #提示输入
stty -echo
#关闭自动打印输入字符的功能
read password < /dev/tty
#读取密码
printf "\nEnter again: "
#换行后提示再输入一次
read password2 < /dev/tty
#再读取一次以确认
printf "\n"
#换行
stty echo
#记着打开自动打印输入字符的功能
echo "Password = " $password #输出读入变量
echo "Password2 = " $password2
echo "All Done"
在vi中保存并退出后执行以下命令:
/> chmod +x test_dev_tty.sh #使该文件成为可执行文件
/> ./test_dev_tty
Enter new
password:
#这里密码的输入被读入到脚本中的password变量
Enter
again:
#这里密码的输入被读入到脚本中的password2变量
Password = hello
Password2 = hello
All Done
二. 简单的命令跟踪:
Linux Shell提供了两种方式来跟踪Shell脚本中的命令,以帮助我们准确的定位程序中存在的问题。下面的代码为第一种方式,该方式会将Shell脚本中所有被执行的命令打印到终端,并在命令前加"+":加号的后面还跟着一个空格。
/> cat > trace_all_command.sh
who | wc
-l
#这两条Shell命令将输出当前Linux服务器登录的用户数量
CTRL +
D
#退出命令行文件编辑状态
/> chmod +x trace_all_command.sh
/> sh -x ./trace_all_command.sh #Shell执行器的-x选项将打开脚本的执行跟踪功能。
+ wc
-l
#被跟踪的两条Shell命令
+ who
2
#实际输出结果。
Linux Shell提供的另一种方式可以只打印部分被执行的Shell命令,该方法在调试较为复杂的脚本时,显得尤为有用。
/> cat > trace_patial_command.sh
#! /bin/bash
set
-x
#从该命令之后打开跟踪功能
echo 1st
echo
#将被打印输出的Shell命令
set
+x
#该Shell命令也将被打印输出,然而在该命令被执行之后,所有的命令将不再打印输出
echo 2nd
echo
#该Shell命令将不再被打印输出。
CTRL + D
#退出命令行文件编辑状态
/> chmod +x
trace_patial_command.sh
/> ./trace_patial_command.sh
+ echo 1st echo
1st echo
+ set +x
2nd echo
三. 正则表达式基本语法描述:
Linux Shell环境下提供了两种正则表达式规则,一个是基本正则表达式(BRE),另一个是扩展正则表达式(ERE)。
下面是这两种表达式的语法列表,需要注意的是,如果没有明确指出的Meta字符,其将可同时用于BRE和ERE,否则将尽适用于指定的模式。
正则元字符 |
模式含义 |
用例 |
\ |
通常用于关闭其后续字符的特殊意义,恢复其原意。 |
\(...\),这里的括号仅仅表示括号。 |
. |
匹配任何单个字符。 |
a.b,将匹配abb、acb等 |
* |
匹配它之前的0-n个的单个字符。 |
a*b,将匹配ab、aab、aaab等。 |
^ |
匹配紧接着的正则表达式,在行的起始处。 |
^ab,将匹配abc、abd等,但是不匹配cab。 |
$ |
匹配紧接着的正则表达式,在行的结尾处。 |
ab$,将匹配ab、cab等,但是不匹配abc。 |
[...] |
方括号表达式,匹配其内部任何字符。其中-表示连续字符的范围,^符号置于方括号里第一个字符则有反向的含义,即匹配不在列表内(方括号)的任何字符。如果想让]和-表示其原意,需要将其放置在方括号的首字符位置,如[]ab]或[-ab],如这两个字符同时存在,则将]放置在首字符位置,-放置在最尾部,如[]ab-]。 |
[a-bA-Z0-9!]表示所有的大小写字母,数字和感叹号。[^abc]表示a、b、c之外的所有字符。[Tt]om,可以匹配Tom和tom。 |
\{n,m\} |
区间表达式,匹配在它前面的单个字符重复出现的次数区间,\{n\}表示重复n次;\{n,\}表示至少重复n次;\{n,m\}表示重复n到m次。 |
ab\{2\}表示abb;ab\{2,\}表示abb、abbb等。ab\{2,4\}表示abb、abbb和abbbb。 |
\(...\) |
将圆括号之间的模式存储在特殊“保留空间”。最多可以将9个独立的子模式存储在单个模式中。匹配于子模式的文本,可以通过转义序列\1到\9,被重复使用在相同模式里。 |
\(ab\).*\1表示ab组合出现两次,两次之间可存在任何数目的任何字符,如abcdab、abab等。 |
{n,m}(ERE) |
其功能等同于上面的\{n,m\},只是不再写\转义符了。 |
ab+匹配ab、abbb等,但是不匹配a。 |
+(ERE) |
和前面的星号相比,+匹配的是前面正则表达式的1-n个实例。 |
|
?(ERE) |
匹配前面正则表达式的0个或1个。 |
ab?仅匹配a或ab。 |
|(ERE) |
匹配于|符号前后的正则表达式。 |
(ab|cd)匹配ab或cd。 |
[:alpha:] |
匹配字母字符。 |
[[:alpha:]!]ab$匹配cab、dab和!ab。 |
[:alnum:] |
匹配字母和数字字符。 |
[[:alnum:]]ab$匹配1ab、aab。 |
[:blank:] |
匹配空格(space)和Tab字符。 |
[[:alnum:]]ab$匹配1ab、aab。 |
[:cntrl:] |
匹配控制字符。 |
|
[:digit:] |
匹配数字字符。 |
|
[:graph:] |
匹配非空格字符。 |
|
[:lower:] |
匹配小写字母字符。 |
|
[:upper:] |
匹配大写字母字符。 |
|
[:punct:] |
匹配标点字符。 |
|
[:space:] |
匹配空白(whitespace)字符。 |
|
[:xdigit:] |
匹配十六进制数字。 |
|
\w |
匹配任何字母和数字组成的字符,等同于[[:alnum:]_] |
|
\W |
匹配任何非字母和数字组成的字符,等同于[^[:alnum:]_] |
|
\<\> |
匹配单词的起始和结尾。 |
\<read匹配readme,me\>匹配readme。 |
下面的列表给出了Linux Shell中常用的工具或命令分别支持的正则表达式的类型。
|
grep |
sed |
vi |
egrep |
awk |
BRE |
* |
* |
* |
|
|
ERE |
|
|
|
* |
* |
四. 使用cut命令选定字段:
cut命令是用来剪下文本文件里的数据,文本文件可以是字段类型或是字符类型。下面给出应用实例:
/> cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
... ...
/> cut -d : -f 1,5 /etc/passwd #-d后面的冒号表示字段之间的分隔符,-f表示取分割后的哪些字段
root:root
#这里取出的是第一个和第五个字段。
bin:bin
daemon:daemon
adm:adm
... ...
/> cut -d: -f 3- /etc/passwd #从第三个字段开始显示,直到最后一个字段。
0:0:root:/root:/bin/bash
1:1:bin:/bin:/sbin/nologin
2:2:daemon:/sbin:/sbin/nologin
3:4:adm:/var/adm:/sbin/nologin
4:7:lp:/var/spool/lpd:/sbin/nologin
... ...
这里需要进一步说明的是,使用cut命令还可以剪切以字符数量为标量的部分字符,该功能通过-c选项实现,其不能与-d选项共存。
/> cut -c 1-4 /etc/passwd #取每行的前1-4个字符。
/> cut -c-4 /etc/passwd #取每行的前4个字符。
root
bin:
daem
adm:
... ...
/> cut -c4- /etc/passwd #取每行的第4个到最后字符。
t:x:0:0:root:/root:/bin/bash
:x:1:1:bin:/bin:/sbin/nologin
mon:x:2:2:daemon:/sbin:/sbin/nologin
:x:3:4:adm:/var/adm:/sbin/nologin
... ...
/> cut -c1,4 /etc/passwd #取每行的第一个和第四个字符。
rt
b:
dm
a:
... ...
/> cut -c1-4,5 /etc/passwd #取每行的1-4和第5个字符。
root:
bin:x
daemo
adm:x
五. 计算行数、字数以及字符数:
Linux提供了一个简单的工具wc用于完成该功能,见如下用例:
/> echo This is a test of the emergency
broadcast system | wc
1 9
49
#1行,9个单词,49个字符
/> echo Testing one two three | wc -c
22
#22个字符
/> echo Testing one two three | wc -l
1
#1行
/> echo Testing one two three | wc -w
4
#4个单词
/> wc /etc/passwd /etc/group #计算两个文件里的数据。
39 71 1933 /etc/passwd
62 62 906 /etc/group
101 133 2839 总用量
六. 提取开头或结尾数行:
有时,你会需要从文本文件里把几行字,多半是靠近开头或结尾的几行提取出来。如查看工作日志等操作。Linux Shell提供head和tail两个命令来完成此项工作。见如下用例:
/> head -n 5 /etc/passwd #显示输入文件的前五行。
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
/> tail -n 5 /etc/passwd #显示输入文件的最后五行。
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/bash
pulse:x:496:494:PulseAudio System
Daemon:/var/run/pulse:/sbin/nologin
gdm:x:42:42::/var/lib/gdm:/sbin/nologin
stephen:x:500:500:stephen:/home/stephen:/bin/bash
如果使用者想查看不间断增长的日志(如服务程序输出的),可以使用tail的-f选项,这样可以让tail命令不会自动退出,必须通过CTRL+C命令强制退出,因此该选项不适合用于Shell脚本中,见如下用例:
/> tail -f -n 5 my_server_log
... ...
^C
#CTRL+C退出到命令行提示符状态。
七. grep家族:
1. grep退出状态:
0: 表示成功;
1: 表示在所提供的文件无法找到匹配的pattern;
2: 表示参数中提供的文件不存在。
见如下示例:
/> grep 'root' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
/> echo $?
0
/> grep 'root1' /etc/passwd #用户root1并不存在
/> echo $?
1
/> grep 'root' /etc/passwd1 #这里的/etc/passwd1文件并不存在
grep: /etc/passwd1: No such file or directory
/> echo $?
2
2. grep中应用正则表达式的实例:
需要说明的是下面所涉及的正则表达式在上一篇中已经给出了详细的说明,因此在看下面例子的时候,可以与前一篇的正则说明部分结合着看。
/> cat testfile
northwest
NW Charles
Main
3.0 .98 3
34
western
WE Sharon
Gray
5.3 .97 5
23
southwest
SW Lewis
Dalsass
2.7 .8 2
18
southern
SO Suan
Chin
5.1 .95
4 15
southeast
SE Patricia
Hemenway 4.0
.7 4 17
eastern
EA TB
Savage
4.4 .84
5 20
northeast
NE AM Main
Jr.
5.1 .94 3
13
north
NO Margot
Weber
4.5 .89
5 9
central
CT Ann
Stephens
5.7 .94 5
13
/> grep NW testfile #打印出testfile中所有包含NW的行。
northwest
NW Charles Main
3.0 .98
3 34
/> grep '^n' testfile #打印出以n开头的行。
northwest
NW Charles Main
3.0 .98
3 34
northeast
NE AM Main Jr.
5.1
.94 3 13
north
NO Margot Weber
4.5 .89 5
9
/> grep '4$' testfile #打印出以4结尾的行。
northwest
NW Charles Main
3.0 .98
3 34
/> grep '5\..' testfile #打印出第一个字符是5,后面跟着一个.字符,再后面是任意字符的行。
western
WE Sharon Gray
5.3
.97 5 23
southern
SO Suan Chin
5.1
.95 4 15
northeast
NE AM Main Jr.
5.1
.94 3 13
central
CT Ann Stephens
5.7 .94
5 13
/> grep '\.5' testfile #打印出所有包含.5的行。
north
NO Margot Weber
4.5 .89
5 9
/> grep '^[we]' testfile #打印出所有以w或e开头的行。
western
WE Sharon Gray
5.3
.97 5 23
eastern
EA TB Savage
4.4
.84 5 20
/> grep '[^0-9]' testfile #打印出所有不是以0-9开头的行。
northwest
NW Charles Main
3.0
.98 3 34
western
WE Sharon Gray
5.3
.97 5 23
southwest
SW Lewis Dalsass
2.7
.8 2 18
southern
SO Suan Chin
5.1
.95 4 15
southeast
SE Patricia Hemenway
4.0 .7
4 17
eastern
EA TB Savage
4.4
.84 5 20
northeast
NE AM Main Jr.
5.1
.94 3 13
north
NO Margot Weber
4.5 .89
5 9
central
CT Ann Stephens
5.7
.94 5 13
/> grep '[A-Z][A-Z] [A-Z]' testfile #打印出所有包含前两个字符是大写字符,后面紧跟一个空格及一个大写字母的行。
eastern
EA TB Savage
4.4 .84
5 20
northeast
NE AM Main Jr.
5.1 .94
3 13
注:在执行以上命令时,如果不能得到预期的结果,即grep忽略了大小写,导致这一问题的原因很可能是当前环境的本地化的设置问题。对于以上命令,如果我将当前语言设置为en_US的时候,它会打印出所有的行,当我将其修改为中文环境时,就能得到我现在的输出了。
/> export LANG=zh_CN #设置当前的语言环境为中文。
/> export LANG=en_US #设置当前的语言环境为美国。
/> export LANG=en_Br #设置当前的语言环境为英国。
/> grep '[a-z]\{9\}' testfile #打印所有包含每个字符串至少有9个连续小写字符的字符串的行。
northwest
NW Charles Main
3.0 .98
3 34
southwest
SW Lewis Dalsass
2.7 .8
2 18
southeast
SE Patricia Hemenway
4.0 .7
4 17
northeast
NE AM Main Jr.
5.1
.94 3 13
#第一个字符是3,紧跟着一个句点,然后是任意一个数字,然后是任意个任意字符,然后又是一个3,然后是制表符,然后又是一个3,需要说明的是,下面正则中的\1表示\(3\)。
/> grep
'\(3\)\.[0-9].*\1 *\1' testfile
northwest
NW Charles Main
3.0 .98
3 34
/> grep '\<north'
testfile #打印所有以north开头的单词的行。
northwest
NW Charles Main
3.0 .98
3 34
northeast
NE AM Main Jr.
5.1
.94 3 13
north
NO Margot Weber 4.5
.89 5 9
/> grep '\<north\>' testfile #打印所有包含单词north的行。
north
NO Margot Weber
4.5 .89
5 9
/> grep '^n\w*' testfile #第一个字符是n,后面是任意字母或者数字。
northwest
NW Charles Main
3.0 .98
3 34
northeast
NE AM Main Jr.
5.1
.94 3 13
north
NO Margot Weber
4.5 .89 5
9
3. 扩展grep(grep -E 或者 egrep):
使用扩展grep的主要好处是增加了额外的正则表达式元字符集。下面我们还是继续使用实例来演示扩展grep。
/> egrep 'NW|EA' testfile #打印所有包含NW或EA的行。如果不是使用egrep,而是grep,将不会有结果查出。
northwest
NW Charles Main
3.0 .98
3 34
eastern
EA TB Savage
4.4
.84 5 20
/> grep 'NW\|EA' testfile #对于标准grep,如果在扩展元字符前面加\,grep会自动启用扩展选项-E。
northwest
NW Charles Main
3.0 .98
3 34
eastern
EA TB Savage
4.4
.84 5 20
/> egrep '3+' testfile
/> grep -E '3+' testfile
/> grep '3\+' testfile #这3条命令将会打印出相同的结果,即所有包含一个或多个3的行。
northwest
NW Charles Main
3.0 .98
3 34
western
WE Sharon Gray
5.3
.97 5 23
northeast
NE AM Main Jr.
5.1
.94 3 13
central
CT Ann Stephens
5.7 .94
5 13
/> egrep '2\.?[0-9]' testfile
/> grep -E '2\.?[0-9]' testfile
/> grep '2\.\?[0-9]' testfile #首先含有2字符,其后紧跟着0个或1个点,后面再是0和9之间的数字。
western
WE Sharon Gray
5.3
.97 5 23
southwest
SW Lewis Dalsass
2.7 .8
2 18
eastern
EA TB Savage
4.4
.84 5 20
/> egrep '(no)+' testfile
/> grep -E '(no)+' testfile
/> grep '\(no\)\+' testfile #3个命令返回相同结果,即打印一个或者多个连续的no的行。
northwest
NW Charles Main
3.0 .98
3 34
northeast NE
AM Main Jr.
5.1 .94
3 13
north
NO Margot Weber
4.5 .89 5
9
/> grep -E '\w+\W+[ABC]' testfile #首先是一个或者多个字母,紧跟着一个或者多个非字母数字,最后一个是ABC中的一个。
northwest
NW Charles Main
3.0 .98
3 34
southern
SO Suan Chin
5.1
.95 4 15
northeast
NE AM Main Jr.
5.1
.94 3 13
central
CT Ann Stephens
5.7 .94
5 13
/> egrep '[Ss](h|u)' testfile
/> grep -E '[Ss](h|u)' testfile
/> grep '[Ss]\(h\|u\)' testfile #3个命令返回相同结果,即以S或s开头,紧跟着h或者u的行。
western
WE Sharon Gray
5.3 .97
5 23
southern
SO Suan Chin
5.1 .95
4 15
/> egrep 'w(es)t.*\1'
testfile #west开头,其中es为\1的值,后面紧跟着任意数量的任意字符,最后还有一个es出现在该行。
northwest
NW Charles Main
3.0 .98
3 34
4. grep选项:
这里先列出grep常用的命令行选项:
选项 |
说明 |
-c |
只显示有多少行匹配,而不具体显示匹配的行。 |
-h |
不显示文件名。 |
-i |
在字符串比较的时候忽略大小写。 |
-l |
只显示包含匹配模板的行的文件名清单。 |
-L |
只显示不包含匹配模板的行的文件名清单。 |
-n |
在每一行前面打印该行在文件中的行数。 |
-v |
反向检索,只显示不匹配的行。 |
-w |
只显示完整单词的匹配。 |
-x |
只显示完整行的匹配。 |
-r/-R |
如果文件参数是目录,该选项将递归搜索该目录下的所有子目录和文件。 |
/> grep -n '^south' testfile #-n选项在每一个匹配行的前面打印行号。
3:southwest
SW Lewis Dalsass
2.7 .8
2 18
4:southern
SO Suan Chin
5.1
.95 4 15
5:southeast
SE Patricia Hemenway
4.0 .7
4 17
/> grep -i 'pat' testfile #-i选项关闭了大小写敏感。
southeast
SE Patricia
Hemenway 4.0
.7 4 17
/> grep -v 'Suan Chin' testfile #打印所有不包含Suan Chin的行。
northwest
NW Charles Main
3.0 .98
3 34
western
WE Sharon Gray
5.3
.97 5 23
southwest
SW Lewis Dalsass
2.7 .8
2 18
southeast
SE Patricia Hemenway
4.0 .7
4 17
eastern EA
TB Savage
4.4 .84 5
20
northeast
NE AM Main Jr.
5.1
.94 3 13
north
NO Margot Weber
4.5 .89 5
9
central
CT Ann Stephens
5.7 .94
5 13
/> grep -l 'ss' testfile #-l使得grep只打印匹配的文件名,而不打印匹配的行。
testfile
/> grep -c 'west' testfile #-c使得grep只打印有多少匹配模板的行。
3
/> grep -w 'north' testfile #-w只打印整个单词匹配的行。
north
NO Margot Weber
4.5 .89
5 9
/> grep -C 2 Patricia testfile #打印匹配行及其上下各两行。
southwest
SW Lewis Dalsass
2.7 .8
2 18
southern
SO Suan Chin
5.1
.95 4 15
southeast
SE Patricia Hemenway
4.0 .7
4 17
eastern
EA TB Savage
4.4
.84 5 20
northeast
NE AM Main Jr.
5.1
.94 3 13
/> grep -B 2 Patricia testfile #打印匹配行及其前两行。
southwest
SW Lewis Dalsass
2.7 .8
2 18
southern
SO Suan Chin
5.1
.95 4 15
southeast
SE Patricia Hemenway
4.0 .7
4 17
/> grep -A 2 Patricia testfile #打印匹配行及其后两行。
southeast
SE Patricia Hemenway
4.0 .7
4 17
eastern
EA TB Savage
4.4
.84 5 20
northeast
NE AM Main Jr.
5.1
.94 3 13
八、流编辑器sed
8.1 sed简介
sed是stream editor的缩写,一种流编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。sed主要用来自动编辑一个或多个文件,简化对文件的反复操作,编写转换程序等。
8.2 sed命令格式
sed [nefri] 'command' file(s)
常用选项:
-n 使用安静(silent)模式。在一般sed的用法中,所有来自stdin的资料一般都会被列出到屏幕,但如果加上-n参数后,则只有经过sed特殊处理的那一行(或者command)才会被列出来。
-e 允许多点编辑。
-f 直接将sed的动作写在一个档案内,-f filename 则可以执行filename内的sed动作。
-r sed 的动作支援的是延伸型正规表示法的语法。(预设是基础正规表示法语法)
-i 直接修改读取的档案内容,而不是由屏幕输出。
常用command:
a\ 新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)~
c\ 取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行!
d 删除,因为是删除啊,所以 d 后面通常不接任何咚咚;
i\ 插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行);
p 列印,亦即将某个选择的资料印出。通常 p 会与参数 sed -n 一起运作~
s 取代,可以直接进行取代的工作哩!通常这个 s 的动作可以搭配正规表示法!例如 1,20s/old/new/g 就是啦!
高级command:
命令 |
功能描述 |
h |
拷贝pattern space的内容到holding buffer(特殊缓冲区)。 |
H |
追加pattern space的内容到holding buffer。 |
g |
获得holding buffer中的内容,并替代当前pattern space中的文本。 |
G |
获得holding buffer中的内容,并追加到当前pattern space的后面。 |
n |
读取下一个输入行,用下一个命令处理新的行而不是用第一个命令。 |
P |
打印pattern space中的第一行。 //大写 |
q |
退出sed。 |
w file |
写并追加pattern space到file的末尾。 |
! |
表示后面的命令对所有没有被选定的行发生作用。 |
s/re/string |
用string替换正则表达式re。 |
= |
打印当前行号码。 |
替换标记 |
|
g |
行内全面替换,如果没有g,只替换第一个匹配。 |
x |
互换pattern space和holding buffer中的文本。 |
y |
把一个字符翻译为另一个字符(但是不能用于正则表达式)。 |
需要说明的是,sed中的正则和grep的基本相同,完全可以参照本系列的第一篇中的详细说明。
8.3 sed实例
# cat testfile
northwest NW Charles Main 3.0 .98 3 34
western WE Sharon Gray 5.3 .97 5 23
southwest SW Lewis Dalsass 2.7 .8 2 18
southern SO Suan Chin 5.1 .95 4 15
southeast SE Patricia Hemenway 4.0 .7 4 17
eastern EA TB Savage 4.4 .84 5 20
northeast NE AM Main Jr. 5.1 .94 3 13
north NO Margot Weber 4.5 .89 5 9
central CT Ann Stephens 5.7 .94 5 13
实例1.1:如果模板north被找到,sed除了打印所有行之外,还有打印匹配行。
# sed '/north/p' testfile
northwest NW Charles Main 3.0 .98 3 34
northwest NW Charles Main 3.0 .98 3 34
western WE Sharon Gray 5.3 .97 5 23
southwest SW Lewis Dalsass 2.7 .8 2 18
southern SO Suan Chin 5.1 .95 4 15
southeast SE Patricia Hemenway 4.0 .7 4 17
eastern EA TB Savage 4.4 .84 5 20
northeast NE AM Main Jr. 5.1 .94 3 13
northeast NE AM Main Jr. 5.1 .94 3 13
north NO Margot Weber 4.5 .89 5 9
north NO Margot Weber 4.5 .89 5 9
central CT Ann Stephens 5.7 .94 5 13
实例1.2:-n选项取消了sed的默认行为。在没有-n的时候,包含模板的行被打印两次,但是在使用-n的时候将只打印包含模板的行。
# sed -n '/north/p' testfile
northwest NW Charles Main 3.0 .98 3 34
northeast NE AM Main Jr. 5.1 .94 3 13
north NO Margot Weber 4.5 .89 5 9
实例:列出第5-7行
# nl testfile |sed -n '5,7p'
5 southeast SE Patricia Hemenway 4.0 .7 4 17
6 eastern EA TB Savage 4.4 .84 5 20
7 northeast NE AM Main Jr. 5.1 .94 3 13
实例2.1:删除第三行,其他行默认输出到屏幕。
# nl testfile |sed '3d'
1 northwest NW Charles Main 3.0 .98 3 34
2 western WE Sharon Gray 5.3 .97 5 23
4 southern SO Suan Chin 5.1 .95 4 15
5 southeast SE Patricia Hemenway 4.0 .7 4 17
6 eastern EA TB Savage 4.4 .84 5 20
7 northeast NE AM Main Jr. 5.1 .94 3 13
8 north NO Margot Weber 4.5 .89 5 9
9 central CT Ann Stephens 5.7 .94 5 13
实例2.2:删除2~5行
# nl testfile |sed '2,5d'
1 northwest NW Charles Main 3.0 .98 3 34
6 eastern EA TB Savage 4.4 .84 5 20
7 northeast NE AM Main Jr. 5.1 .94 3 13
8 north NO Margot Weber 4.5 .89 5 9
9 central CT Ann Stephens 5.7 .94 5 13
实例2.3:从第三行删除到最后一行,其他行被打印。$表示最后一行。
# nl testfile |sed '3,$d'
1 northwest NW Charles Main 3.0 .98 3 34
2 western WE Sharon Gray 5.3 .97 5 23
实例2.4:删除最后一行,其他行打印。
# nl testfile |sed '$d'
1 northwest NW Charles Main 3.0 .98 3 34
2 western WE Sharon Gray 5.3 .97 5 23
3 southwest SW Lewis Dalsass 2.7 .8 2 18
4 southern SO Suan Chin 5.1 .95 4 15
5 southeast SE Patricia Hemenway 4.0 .7 4 17
6 eastern EA TB Savage 4.4 .84 5 20
7 northeast NE AM Main Jr. 5.1 .94 3 13
8 north NO Margot Weber 4.5 .89 5 9
实例2.5:删除所有包含north的行,其他行打印。
# nl testfile |sed '/north/d'
2 western WE Sharon Gray 5.3 .97 5 23
3 southwest SW Lewis Dalsass 2.7 .8 2 18
4 southern SO Suan Chin 5.1 .95 4 15
5 southeast SE Patricia Hemenway 4.0 .7 4 17
6 eastern EA TB Savage 4.4 .84 5 20
9 central CT Ann Stephens 5.7 .94 5 13
实例3.1:在第二行后(即加在第三行)加上"United States"。
# nl testfile |sed '2a United States'
1 northwest NW Charles Main 3.0 .98 3 34
2 western WE Sharon Gray 5.3 .97 5 23
United States
3 southwest SW Lewis Dalsass 2.7 .8 2 18
4 southern SO Suan Chin 5.1 .95 4 15
5 southeast SE Patricia Hemenway 4.0 .7 4 17
6 eastern EA TB Savage 4.4 .84 5 20
7 northeast NE AM Main Jr. 5.1 .94 3 13
8 north NO Margot Weber 4.5 .89 5 9
9 central CT Ann Stephens 5.7 .94 5 13
如果要在第二行前加,则命令为
# nl testfile |sed '2i United States'
实例3.2:在第二行后加上两行文本。
# nl testfile
|sed '2a United States \
> America'
1 northwest NW Charles Main 3.0 .98 3 34
2 western WE Sharon Gray 5.3 .97 5 23
United States
America
3 southwest SW Lewis Dalsass 2.7 .8 2 18
4 southern SO Suan Chin 5.1 .95 4 15
5 southeast SE Patricia Hemenway 4.0 .7 4 17
6 eastern EA TB Savage 4.4 .84 5 20
7 northeast NE AM Main Jr. 5.1 .94 3 13
8 north NO Margot Weber 4.5 .89 5 9
9 central CT Ann Stephens 5.7 .94 5 13
若要新增两行,则每一行之间都必须要以反斜线“\”来进行新行的增加。
实例4.1:将第2~5行的内容取代为“No 2-5 number”。
# nl testfile |sed '2,5c No 2-5 number'
1 northwest NW Charles Main 3.0 .98 3 34
No 2-5 number
6 eastern EA TB Savage 4.4 .84 5 20
7 northeast NE AM Main Jr. 5.1 .94 3 13
8 north NO Margot Weber 4.5 .89 5 9
9 central CT Ann Stephens 5.7 .94 5 13
实例3.1:s表示替换,g表示命令作用于整个当前行。如果该行存在多个west,都将被替换为north,如果没有g,则只是替换第一个匹配。
格式:sed 's/要替换的字符串/新的字符串/g'
# nl testfile |sed 's/west/north/g'
1 northnorth NW Charles Main 3.0 .98 3 34
2 northern WE Sharon Gray 5.3 .97 5 23
3 southnorth SW Lewis Dalsass 2.7 .8 2 18
4 southern SO Suan Chin 5.1 .95 4 15
5 southeast SE Patricia Hemenway 4.0 .7 4 17
6 eastern EA TB Savage 4.4 .84 5 20
7 northeast NE AM Main Jr. 5.1 .94 3 13
8 north NO Margot Weber 4.5 .89 5 9
9 central CT Ann Stephens 5.7 .94 5 13
实例3.2:-n表示只打印匹配行,如果某一行的开头是west,则替换为north。
# sed -n 's/^west/north/p' testfile
northern WE Sharon Gray 5.3 .97 5 23
实例3.3:&符号表示替换字符串中被找到的部分。所有以两个数字结束的行,最后的数字都将被它们自己替换,同时追加.5。
# nl testfile |sed 's/[0-9][0-9]$/&.5/'
1 northwest NW Charles Main 3.0 .98 3 34.5
2 western WE Sharon Gray 5.3 .97 5 23.5
3 southwest SW Lewis Dalsass 2.7 .8 2 18.5
4 southern SO Suan Chin 5.1 .95 4 15.5
5 southeast SE Patricia Hemenway 4.0 .7 4 17.5
6 eastern EA TB Savage 4.4 .84 5 20.5
7 northeast NE AM Main Jr. 5.1 .94 3 13.5
8 north NO Margot Weber 4.5 .89 5 9
9 central CT Ann Stephens 5.7 .94 5 13.5
实例3.4:所有的Hemenway被替换为Jones。-n选项加p命令则表示只打印匹配行。
# nl testfile |sed -n 's/Hemenway/Jones/gp'
5 southeast SE Patricia Jones 4.0 .7 4 17
实例3.5:模板Mar被包含在一对括号中,并在特殊的寄存器中保存为tag 1,它将在后面作为\1替换字符串,Margot被替换为Marlianne。
# nl testfile |sed -n 's/\(Mar\)got/\1lianne/p'
8 north NO Marlianne Weber 4.5 .89 5 9
实例3.6:s后面的字符一定是分隔搜索字符串和替换字符串的分隔符,默认为斜杠,但是在s命令使用的情况下可以改变。不论什么字符紧跟着s命令都认为是新的分隔符。这个技术在搜索含斜杠的模板时非常有用,例如搜索时间和路径的时候。
# sed 's#3#88#g' testfile
northwest NW Charles Main 88.0 .98 88 884
western WE Sharon Gray 5.88 .97 5 288
southwest SW Lewis Dalsass 2.7 .8 2 18
southern SO Suan Chin 5.1 .95 4 15
southeast SE Patricia Hemenway 4.0 .7 4 17
eastern EA TB Savage 4.4 .84 5 20
northeast NE AM Main Jr. 5.1 .94 88 188
north NO Margot Weber 4.5 .89 5 9
central CT Ann Stephens 5.7 .94 5 188
# sed 's@3@88@g' testfile
northwest NW Charles Main 88.0 .98 88 884
western WE Sharon Gray 5.88 .97 5 288
southwest SW Lewis Dalsass 2.7 .8 2 18
southern SO Suan Chin 5.1 .95 4 15
southeast SE Patricia Hemenway 4.0 .7 4 17
eastern EA TB Savage 4.4 .84 5 20
northeast NE AM Main Jr. 5.1 .94 88 188
north NO Margot Weber 4.5 .89 5 9
central CT Ann Stephens 5.7 .94 5 188
实例3.7:所有在模板west和east所确定的范围内的行都被打印,如果west出现在east后面的行中,从west开始到下一个east,无论这个 east出现在哪里,二者之间的行都被打印,即使从west开始到文件的末尾还没有出现east,那么从west到末尾的所有行都将打印。
# nl testfile |sed -n '/south/,/east/p'
3 southwest SW Lewis Dalsass 2.7 .8 2 18
4 southern SO Suan Chin 5.1 .95 4 15
5 southeast SE Patricia Hemenway 4.0 .7 4 17
实例3.8:打印从第五行开始到第一个以northeast开头的行之间的所有行。
# sed -n '5,/^northeast/p' testfile
southeast SE Patricia Hemenway 4.0 .7 4 17
eastern EA TB Savage 4.4 .84 5 20
northeast NE AM Main Jr. 5.1 .94 3 13
实例3.9:-e选项表示多点编辑。第一个编辑命令是删除第一到第三行。第二个编辑命令是用Jones替换Hemenway。
# nl testfile |sed -e '1,3d' -e 's/Hemenway/Jones/'
4 southern SO Suan Chin 5.1 .95 4 15
5 southeast SE Patricia Jones 4.0 .7 4 17
6 eastern EA TB Savage 4.4 .84 5 20
7 northeast NE AM Main Jr. 5.1 .94 3 13
8 north NO Margot Weber 4.5 .89 5 9
9 central CT Ann Stephens 5.7 .94 5 13
实例3.10:将所有匹配含有north的行写入newfile中。
# sed -n '/north/w newfile' testfile
# cat newfile
northwest NW Charles Main 3.0 .98 3 34
northeast NE AM Main Jr. 5.1 .94 3 13
north NO Margot Weber 4.5 .89 5 9
实例4.1:i是插入命令,在匹配模式行前插入文本。
# sed '/eastern/i\NEW ENGLAND REGION' testfile
northwest NW Charles Main 3.0 .98 3 34
western WE Sharon Gray 5.3 .97 5 23
southwest SW Lewis Dalsass 2.7 .8 2 18
southern SO Suan Chin 5.1 .95 4 15
southeast SE Patricia Hemenway 4.0 .7 4 17
NEW ENGLAND REGION
eastern EA TB Savage 4.4 .84 5 20
northeast NE AM Main Jr. 5.1 .94 3 13
north NO Margot Weber 4.5 .89 5 9
central CT Ann Stephens 5.7 .94 5 13
实例5.1:找到匹配模式eastern的行后,执行后面花括号中的一组命令,每个命令之间用逗号分隔,n表示定位到匹配行的下一行,s/AM/Archie/完成Archie到AM的替换,p和-n选项的合用,则只是打印作用到的行。
# sed -n '/eastern/{n;s/AM/Archie/;p}' testfile
northeast NE Archie Main Jr. 5.1 .94 3 13
实例:-e表示多点编辑,第一个编辑命令y将前三行中的所有小写字母替换为大写字母,-n表示不显示替换后的输出,第二个编辑命令将只是打印输出转换后的前三行。注意y不能用于正则。
# sed -n -e '1,3y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' -e '1,3p' testfile
NORTHWEST NW CHARLES MAIN 3.0 .98 3 34
WESTERN WE SHARON GRAY 5.3 .97 5 23
SOUTHWEST SW LEWIS DALSASS 2.7 .8 2 18
实例:打印完第二行后退出。
# sed '2q' testfile
northwest NW Charles Main 3.0 .98 3 34
western WE Sharon Gray 5.3 .97 5 23
实例:当模板Lewis在某一行被匹配,替换命令首先将Lewis替换为Joseph,然后再用q退出sed。
# sed '/Lewis/{s/Lewis/Joseph/;q;}' testfile
northwest NW Charles Main 3.0 .98 3 34
western WE Sharon Gray 5.3 .97 5 23
southwest SW Joseph Dalsass 2.7 .8 2 18
实例:在sed处理文件的时候,每一行都被保存在pattern space的临时缓冲区中。除非行被删除或者输出被取消,否则所有被处理过的行都将打印在屏幕上。接着pattern space被清空,并存入新的一行等待处理。在下面的例子中,包含模板的northeast行被找到,并被放入pattern space中,h命令将其复制并存入一个称为holding buffer的特殊缓冲区内。在第二个sed编辑命令中,当达到最后一行后,G命令告诉sed从holding buffer中取得该行,然后把它放回到pattern space中,且追加到现在已经存在于模式空间的行的末尾。
# sed -e '/northeast/h' -e '$G' testfile
northwest NW Charles Main 3.0 .98 3 34
western WE Sharon Gray 5.3 .97 5 23
southwest SW Lewis Dalsass 2.7 .8 2 18
southern SO Suan Chin 5.1 .95 4 15
southeast SE Patricia Hemenway 4.0 .7 4 17
eastern EA TB Savage 4.4 .84 5 20
northeast NE AM Main Jr. 5.1 .94 3 13
north NO Margot Weber 4.5 .89 5 9
central CT Ann Stephens 5.7 .94 5 13
northeast NE AM Main Jr. 5.1 .94 3 13
实例:如果模板WE在某一行被匹配,h命令将使得该行从pattern space中复制到holding buffer中,d命令在将该行删除,因此WE匹配行没有在原来的位置被输出。第二个命令搜索CT,一旦被找到,G命令将从holding buffer中取回行,并追加到当前pattern space的行末尾。简单的说,WE所在的行被移动并追加到包含CT行的后面。
# sed -e '/WE/{h;d;}' -e '/CT/{G;}' testfile
northwest NW Charles Main 3.0 .98 3 34
southwest SW Lewis Dalsass 2.7 .8 2 18
southern SO Suan Chin 5.1 .95 4 15
southeast SE Patricia Hemenway 4.0 .7 4 17
eastern EA TB Savage 4.4 .84 5 20
northeast NE AM Main Jr. 5.1 .94 3 13
north NO Margot Weber 4.5 .89 5 9
central CT Ann Stephens 5.7 .94 5 13
western WE Sharon Gray 5.3 .97 5 23
实例:第一个命令将匹配northeast的行从pattern space复制到holding buffer,第二个命令在读取的文件的末尾时,g命令告诉sed从holding buffer中取得行,并把它放回到pattern space中,以替换已经存在于pattern space中的。简单说就是包含模板northeast的行被复制并覆盖了文件的末尾行。
# sed -e '/northeast/h' -e '$g' testfile
northwest NW Charles Main 3.0 .98 3 34
western WE Sharon Gray 5.3 .97 5 23
southwest SW Lewis Dalsass 2.7 .8 2 18
southern SO Suan Chin 5.1 .95 4 15
southeast SE Patricia Hemenway 4.0 .7 4 17
eastern EA TB Savage 4.4 .84 5 20
northeast NE AM Main Jr. 5.1 .94 3 13
north NO Margot Weber 4.5 .89 5 9
northeast NE AM Main Jr. 5.1 .94 3 13
实例:模板WE匹配的行被h命令复制到holding buffer,再被d命令删除。结果可以看出WE的原有位置没有输出。第二个编辑命令将找到匹配CT的行,g命令将取得holding buffer中的行,并覆盖当前pattern space中的行,即匹配CT的行。简单的说,任何包含模板northeast的行都将被复制,并覆盖包含CT的行。
# sed -e '/WE/{h;d;}' -e '/CT/{g;}' testfile
northwest NW Charles Main 3.0 .98 3 34
southwest SW Lewis Dalsass 2.7 .8 2 18
southern SO Suan Chin 5.1 .95 4 15
southeast SE Patricia Hemenway 4.0 .7 4 17
eastern EA TB Savage 4.4 .84 5 20
northeast NE AM Main Jr. 5.1 .94 3 13
north NO Margot Weber 4.5 .89 5 9
western WE Sharon Gray 5.3 .97 5 23
实例:第一个编辑中的h命令将匹配Patricia的行复制到holding buffer中,第二个编辑中的x命令,会将holding buffer中的文本考虑到pattern space中,而pattern space中的文本被复制到holding buffer中。因此在打印匹配Margot行的地方打印了holding buffer中的文本,即第一个命令中匹配Patricia的行文本,第三个编辑命令会将交互后的holding buffer中的文本在最后一行的后面打印出来。
# sed -e '/Patricia/h' -e '/Margot/x' -e '$G' testfile
northwest NW Charles Main 3.0 .98 3 34
western WE Sharon Gray 5.3 .97 5 23
southwest SW Lewis Dalsass 2.7 .8 2 18
southern SO Suan Chin 5.1 .95 4 15
southeast SE Patricia Hemenway 4.0 .7 4 17
eastern EA TB Savage 4.4 .84 5 20
northeast NE AM Main Jr. 5.1 .94 3 13
southeast SE Patricia Hemenway 4.0 .7 4 17
central CT Ann Stephens 5.7 .94 5 13
north NO Margot Weber 4.5 .89 5 9
九. awk实用功能:
和sed一样,awk也是逐行扫描文件的,从第一行到最后一行,寻找匹配特定模板的行,并在这些行上运行“选择”动作。如果一个模板没有指定动作,这些匹配的行就被显示在屏幕上。如果一个动作没有模板,所有被动作指定的行都被处理。
1. awk的基本格式:
/> awk 'pattern' filename
/> awk '{action}' filename
/> awk 'pattern {action}' filename
具体应用方式分别见如下三个用例:
/> cat employees
Tom Jones
4424 5/12/66
543354
Mary Adams
5346 11/4/63
28765
Sally Chang
1654 7/22/54
650000
Billy Black
1683 9/23/44
336500
/> awk '/Mary/' employees #打印所有包含模板Mary的行。
Mary Adams
5346 11/4/63
28765
#打印文件中的第一个字段,这个域在每一行的开始,缺省由空格或其它分隔符。
/> awk '{print $1}' employees
Tom
Mary
Sally
Billy
/> awk '/Sally/{print $1, $2}' employees #打印包含模板Sally的行的第一、第二个域字段。
Sally Chang
2. awk的格式输出:
awk中同时提供了print和printf两种打印输出的函数,其中print函数的参数可以是变量、数值或者字符串。字符串必须用双引号引用,参数 用逗号分隔。如果没有逗号,参数就串联在一起而无法区分。这里,逗号的作用与输出文件的分隔符的作用是一样的,只是后者是空格而已。下面给出基本的转码序 列:
转码 |
含义 |
\n |
换行 |
\r |
回车 |
\t |
制表符 |
/> date | awk '{print "Month: " $2
"\nYear: ", $6}'
Month: Oct
Year: 2011
/> awk '/Sally/{print "\t\tHave a nice day,
" $1,$2 "\!"}' employees
Have a nice day, Sally Chang!
在打印数字的时候你也许想控制数字的格式,我们通常用printf来完成这个功能。awk的特殊变量OFMT也可以在使用print函数的时候,控制数字的打印格式。它的默认值是"%.6g"----小数点后面6位将被打印。
/> awk 'BEGIN { OFMT="%.2f"; print
1.2456789, 12E-2}'
1.25 0.12
现在我们介绍一下功能更为强大的printf函数,其用法和c语言中printf基本相似。下面我们给出awk中printf的格式化说明符列表:
格式化说明符 |
功能 |
示例 |
结果 |
%c |
打印单个ASCII字符。 |
printf("The character is %c.\n",x) |
The character is A. |
%d |
打印十进制数。 |
printf("The boy is %d years old.\n",y) |
The boy is 15 years old. |
%e |
打印用科学记数法表示的数。 |
printf("z is %e.\n",z) |
z is 2.3e+01. |
%f |
打印浮点数。 |
printf("z is %f.\n",z) |
z is 2.300000 |
%o |
打印八进制数。 |
printf("y is %o.\n",y) |
y is 17. |
%s |
打印字符串。 |
printf("The name of the culprit is %s.\n",$1); |
The name of the culprit is Bob Smith. |
%x |
打印十六进制数。 |
printf("y is %x.\n",y) |
y is f. |
注:假设列表中的变脸值为x = A, y = 15, z = 2.3, $1 = "Bob
Smith"
/> echo "Linux" | awk '{printf
"|%-15s|\n", $1}' # %-15s表示保留15个字符的空间,同时左对齐。
|Linux
|
/> echo "Linux" | awk '{printf
"|%15s|\n", $1}' # %-15s表示保留15个字符的空间,同时右对齐。
|
Linux|
#%8d表示数字右对齐,保留8个字符的空间。
/> awk '{printf "The name is %-15s
ID is %8d\n", $1,$3}' employees
The name is
Tom ID
is 4424
The name is
Mary ID
is 5346
The name is
Sally ID
is 1654
The name is
Billy
ID is 1683
3. awk中的记录和域:
awk中默认的记录分隔符是回车,保存在其内建变量ORS和RS中。$0变量是指整条记录。
/> awk '{print $0}' employees #这等同于print的默认行为。
Tom Jones
4424 5/12/66
543354
Mary Adams
5346 11/4/63
28765
Sally Chang
1654 7/22/54
650000
Billy Black
1683 9/23/44
336500
变量NR(Number of
Record),记录每条记录的编号。
/> awk '{print NR, $0}' employees
1 Tom Jones
4424 5/12/66
543354
2 Mary Adams
5346 11/4/63
28765
3 Sally Chang
1654 7/22/54
650000
4 Billy
Black 1683
9/23/44 336500
变量NF(Number of
Field),记录当前记录有多少域。
/> awk '{print $0,NF}' employees
Tom Jones
4424
5/12/66
543354 5
Mary Adams
5346 11/4/63
28765 5
Sally Chang
1654
7/22/54
650000 5
Billy Black
1683
9/23/44
336500 5
#根据employees生成employees2。sed的用法可以参考上一篇blog。
/> sed 's/[[:space:]]\+\([0-9]\)/:\1/g;w
employees2' employees
/> cat employees
Tom Jones:4424:5/12/66:543354
Mary Adams:5346:11/4/63:28765
Sally Chang:1654:7/22/54:650000
Billy Black:1683:9/23/44:336500
/> awk -F: '/Tom Jones/{print $1,$2}'
employees2 #这里-F选项后面的字符表示分隔符。
Tom Jones 4424
变量OFS(Output Field
Seperator)表示输出字段间的分隔符,缺省是空格。
/> awk -F: '{OFS = "?"};
/Tom/{print $1,$2 }' employees2 #在输出时,域字段间的分隔符已经是?(问号)了
Tom Jones?4424
对于awk而言,其模式部分将控制这动作部分的输入,只有符合模式条件的记录才可以交由动作部分基础处理,而模式部分不仅可以写成正则表达式(如上面的例子),awk还支持条件表达式,如:
/> awk '$3 < 4000 {print}' employees
Sally Chang 1654
7/22/54 650000
Billy Black
1683 9/23/44
336500
在花括号内,用分号分隔的语句称为动作。如果模式在动作前面,模式将决定什么时候发出动作。动作可以是一个语句或是一组语句。语句之间用分号分隔,也可以用换行符,如:
pattern { action statement; action statement; etc. } or
pattern {
action statement
action statement
}
模式和动作一般是捆绑在一起的。需要注意的是,动作是花括号内的语句。模式控制的动作是从第一个左花括号开始到第一个右花括号结束,如下:
/> awk '$3 < 4000 && /Sally/
{print}' employees
Sally Chang 1654
7/22/54 650000
4. 匹配操作符:
" ~ " 用来在记录或者域内匹配正则表达式。
/> awk '$1 ~ /[Bb]ill/'
employees #显示所有第一个域匹配Bill或bill的行。
Billy Black 1683
9/23/44 336500
/> awk '$1 !~ /[Bb]ill/'
employees #显示所有第一个域不匹配Bill或bill的行,其中!~表示不匹配的意思。
Tom Jones
4424 5/12/66
543354
Mary Adams
5346 11/4/63
28765
Sally Chang
1654 7/22/54
650000
5. awk的基本应用实例:
/> cat testfile
northwest NW
Charles Main
3.0 .98
3 34
western WE
Sharon Gray
5.3
.97 5
23
southwest SW
Lewis Dalsass
2.7 .8
2 18
southern SO
Suan Chin
5.1
.95 4
15
southeast SE
Patricia Hemenway 4.0
.7
4 17
eastern EA
TB Savage
4.4
.84 5
20
northeast NE
AM Main Jr.
5.1
.94 3
13
north
NO Margot
Weber 4.5
.89 5
9
central
CT Ann Stephens
5.7
.94 5
13
/> awk '/^north/' testfile #打印所有以north开头的行。
northwest
NW Charles Main
3.0 .98
3 34
northeast
NE AM Main Jr.
5.1 .94
3 13
north
NO Margot Weber
4.5 .89
5 9
/> awk '/^(no|so)/' testfile #打印所有以so和no开头的行。
northwest
NW Charles
Main
3.0 .98 3
34
southwest
SW Lewis
Dalsass
2.7 .8
2 18
southern
SO Suan Chin
5.1 .95
4 15
southeast
SE Patricia
Hemenway 4.0
.7 4 17
northeast
NE AM Main
Jr.
5.1 .94
3 13
north
NO Margot Weber
4.5 .89
5 9
/> awk '$5 ~ /\.[7-9]+/' testfile #第五个域字段匹配包含.(点),后面是7-9的数字。
southwest
SW Lewis
Dalsass
2.7 .8 2
18
central
CT Ann
Stephens
5.7 .94
5 13
/> awk '$8 ~ /[0-9][0-9]$/{print $8}' testfile #第八个域以两个数字结束的打印。
34
23
18
15
17
20
13
十. awk表达式功能:
1. 比较表达式:
比较表达式匹配那些只在条件为真时才运行的行。这些表达式利用关系运算符来比较数字和字符串。见如下awk支持的条件表达式列表:
运算符 |
含义 |
例子 |
< |
小于 |
x < y |
<= |
小于等于 |
x <= y |
== |
等于 |
x == y |
!= |
不等于 |
x != y |
>= |
大于等于 |
x >= y |
> |
大于 |
x > y |
~ |
匹配 |
x ~ /y/ |
!~ |
不匹配 |
x !~ /y/ |
/> cat employees
Tom Jones
4424 5/12/66
543354
Mary Adams
5346 11/4/63
28765
Sally Chang 1654
7/22/54 650000
Billy Black
1683 9/23/44
336500
/> awk '$3 == 5346'
employees #打印第三个域等于5346的行。
Mary Adams
5346 11/4/63
28765
/> awk '$3 > 5000 {print $1}'
employees #打印第三个域大于5000的行的第一个域字段。
Mary
/> awk '$2 ~ /Adam/' employess #打印第二个域匹配Adam的行。
Mary Adams
5346 11/4/63
28765
2. 条件表达式:
条件表达式使用两个符号--问号和冒号给表达式赋值: conditional
expression1 ? expression2 : expressional3,其逻辑等同于C语言中的条件表达式。其对应的if/else语句如下:
{
if (expression1)
expression2
else
expression3
}
/> cat testfile
northwest NW
Charles Main
3.0
.98 3
34
western WE
Sharon Gray
5.3 .97
5 23
southwest SW
Lewis Dalsass
2.7 .8
2 18
southern SO
Suan Chin
5.1
.95 4
15
southeast SE
Patricia Hemenway 4.0
.7
4 17
eastern EA
TB Savage
4.4
.84 5
20
northeast NE
AM Main Jr.
5.1
.94 3
13
north
NO Margot
Weber 4.5
.89 5
9
central
CT Ann Stephens
5.7
.94 5
13
/> awk 'NR <= 3 {print ($7 > 4 ?
"high "$7 : "low "$7) }' testfile
low 3
high 5
low 2
3. 数学表达式:
运算可以在模式内进行,其中awk将所有的运算都视为浮点运算,见如下列表:
运算符 |
含义 |
例子 |
+ |
加 |
x + y |
- |
减 |
x - y |
* |
乘 |
x * y |
/ |
除 |
x / y |
% |
取余 |
x % y |
^ |
乘方 |
x ^ y |
/> awk '/southern/{print $5 + 10}' testfile #如果记录包含正则表达式southern,第五个域就加10并打印。
15.1
/> awk '/southern/{print $8 /2 }'
testfile #如果记录包含正则表达式southern,第八个域除以2并打印。
7.5
4. 逻辑表达式:
见如下列表:
运算符 |
含义 |
例子 |
&& |
逻辑与 |
a && b |
|| |
逻辑或 |
a || b |
! |
逻辑非 |
!a |
/> awk '$8 > 10 && $8 < 17' testfile #打印出第八个域的值大于10小于17的记录。
southern
SO Suan Chin
5.1 .95
4 15
central
CT Ann
Stephens
5.7 .94 5
13
#打印第二个域等于NW,或者第一个域匹配south的行的第一、第二个域。
/> awk '$2 == "NW" || $1 ~ /south/ {print $1,$2}' testfile
northwest NW
southwest SW
southern SO
southeast SE
/> awk '!($8 > 13) {print $8}' testfile #打印第八个域字段不大于13的行的第八个域。
3
9
13
5. 范围模板:
范围模板匹配从第一个模板的第一次出现到第二个模板的第一次出现,第一个模板的下一次出现到第一个模板的下一次出现等等。如果第一个模板匹配而第二个模板没有出现,awk就显示到文件末尾的所有行。
/> awk '/^western/,/^eastern/ {print $1}' testfile #打印以western开头到eastern开头的记录的第一个域。
western WE
southwest SW
southern SO
southeast SE
eastern EA
6. 赋值符号:
#找到第三个域等于Ann的记录,然后给该域重新赋值为Christian,之后再打印输出该记录。
/> awk '$3 == "Ann" { $3 = "Christian"; print}'
testfile
central CT Christian Stephens 5.7 .94 5 13
/> awk '/Ann/{$8 += 12; print $8}' testfile #找到包含Ann的记录,并将该条记录的第八个域的值+=12,最后再打印输出。
25
十一. awk编程:
1. 变量:
在awk中变量无须定义即可使用,变量在赋值时即已经完成了定义。变量的类型可以是数字、字符串。根据使用的不同,未初始化变量的值为0或空白字符串" ",这主要取决于变量应用的上下文。下面为变量的赋值负号列表:
符号 |
含义 |
等价形式 |
= |
a = 5 |
a = 5 |
+= |
a = a + 5 |
a += 5 |
-= |
a = a - 5 |
a -= 5 |
*= |
a = a * 5 |
a *= 5 |
/= |
a = a / 5 |
a /= 5 |
%= |
a = a % 5 |
a %= 5 |
^= |
a = a ^ 5 |
a ^= 5 |
/> awk '$1 ~ /Tom/ {Wage = $2 * $3; print Wage}' filename
该命令将从文件中读取,并查找第一个域字段匹配Tom的记录,再将其第二和第三个字段的乘积赋值给自定义的Wage变量,最后通过print命令将该变量打印输出。
/> awk ' {$5 = 1000 * $3 / $2; print}' filename
在上面的命令中,如果$5不存在,awk将计算表达式1000 * $3 / $2的值,并将其赋值给$5。如果第五个域存在,则用表达式覆盖$5原来的值。
我们同样也可以在命令行中定义自定义的变量,用法如下:
/> awk -F: -f awkscript month=4 year=2011
filename
这里的month和year都是自定义变量,且分别被赋值为4和2000,在awk的脚本中这些变量将可以被直接使用,他们和脚本中定义的变量在使用上没有任何区别。
除此之外,awk还提供了一组内建变量(变量名全部大写),见如下列表:
变量名 |
变量内容 |
ARGC |
命令行参数的数量。 |
ARGIND |
命令行正在处理的当前文件的AGV的索引。 |
ARGV |
命令行参数数组。 |
CONVFMT |
转换数字格式。 |
ENVIRON |
从shell中传递来的包含当前环境变量的数组。 |
ERRNO |
当使用close函数或者通过getline函数读取的时候,发生的重新定向错误的描述信息就保存在这个变量中。 |
FIELDWIDTHS |
在对记录进行固定域宽的分割时,可以替代FS的分隔符的列表。 |
FILENAME |
当前的输入文件名。 |
FNR |
当前文件的记录号。 |
FS |
输入分隔符,默认是空格。 |
IGNORECASE |
在正则表达式和字符串操作中关闭大小写敏感。 |
NF |
当前文件域的数量。 |
NR |
当前文件记录数。 |
OFMT |
数字输出格式。 |
OFS |
输出域分隔符。 |
ORS |
输出记录分隔符。 |
RLENGTH |
通过match函数匹配的字符串的长度。 |
RS |
输入记录分隔符。 |
RSTART |
通过match函数匹配的字符串的偏移量。 |
SUBSEP |
下标分隔符。 |
/> cat employees2
Tom Jones:4424:5/12/66:543354
Mary Adams:5346:11/4/63:28765
Sally Chang:1654:7/22/54:650000
Mary Black:1683:9/23/44:336500
/> awk -F: '{IGNORECASE = 1}; $1 == "mary
adams" { print NR, $1, $2, $NF}' employees2
2 Mary Adams 5346 28765
/> awk -F: ' $1 == "mary adams" { print
NR, $1, $2, $NF}' employees2
没有输出结果。
当IGNORECASE内置变量的值为非0时,表示在进行字符串操作和处理正则表达式时关闭大小写敏感。这里的"mary adams"将匹配文件中的"Mary Admams"记录。最后print打印出第一、第二和最后一个域。需要说明的是NF表示当前记录域的数量,因此$NF将表示最后一个域的值。
awk在动作部分还提供了BEGIN块和END块。其中BEGIN动作块在awk处理任何输入文件行之前执行。事实上,BEGIN块可以在没有任何输入 文件的条件下测试。因为在BEGIN块执行完毕以前awk将不读取任何输入文件。BEGIN块通常被用来改变内建变量的值,如OFS、RS或FS等。也可 以用于初始化自定义变量值,或打印输出标题。
/> awk 'BEGIN {FS = ":"; OFS =
"\t"; ORS = "\n\n"} { print $1,$2,$3} filename
上例中awk在处理文件之前,已经将域分隔符(FS)设置为冒号,输出文件域分隔符(OFS)设置为制表符,输出记录分隔符(ORS)被设置为两个换行符。BEGIN之后的动作模块中如果有多个语句,他们之间用分号分隔。
和BEGIN恰恰相反,END模块中的动作是在整个文件处理完毕之后被执行的。
/> awk 'END {print "The number of the
records is " NR }' filename
awk在处理输入文件之后,执行END模块中的动作,上例中NR的值是读入的最后一个记录的记录号。
/> awk '/Mary/{count++} END{print "Mary
was found " count " times." }' employees2
Mary was found 2 times.
/> awk '/Mary/{count++} END{print "Mary was found " count
" times." }' employees2
Mary was found 2 times.
/> cat testfile
northwest
NW Charles
Main
3.0 .98
3 34
western
WE Sharon
Gray
5.3 .97
5 23
southwest
SW Lewis
Dalsass
2.7 .8 2
18
southern
SO Suan
Chin
5.1 .95
4 15
southeast
SE Patricia
Hemenway 4.0
.7 4 17
eastern
EA TB
Savage
4.4 .84
5 20
northeast
NE AM Main
Jr.
5.1 .94
3 13
north
NO Margot
Weber
4.5 .89
5 9
central
CT Ann
Stephens
5.7 .94
5 13
/> awk '/^north/{count += 1; print count}'
testfile #如记录以正则north开头,则创建变量count同时增一,再输出其值。
1
2
3
#这里只是输出前三个字段,其中第七个域先被赋值给变量x,在自减一,最后再同时打印出他们。
/> awk 'NR <= 3 {x = $7--;
print "x = " x ", $7 = " $7}' testfile
x = 3, $7 = 2
x = 5, $7 = 4
x = 2, $7 = 1
#打印NR(记录号)的值在2--5之间的记录。
/> awk 'NR == 2,NR == 5 {print
"The record number is " NR}' testfile
The record number is 2
The record number is 3
The record number is 4
The record number is 5
#打印环境变量USER和HOME的值。环境变量的值由父进程shell传递给awk程序的。
/> awk 'BEGIN { print
ENVIRON["USER"],ENVIRON["HOME"]}'
root /root
#BEGIN块儿中对OFS内置变量重新赋值了,因此后面的输出域分隔符改为了\t。
/> awk 'BEGIN { OFS =
"\t"}; /^Sharon/{ print $1,$2,$7}' testfile
western WE 5
#从输入文件中找到以north开头的记录count就加一,最后在END块中输出该变量。
/> awk '/^north/{count++};
END{print count}' testfile
3
2. 重新定向:
在 动作语句中使用shell通用的重定向输出符号">"就可以完成awk的重定向操作,当使用>的时候,原有文件将被清空,同时文件持续打开, 直到文件被明确的关闭或者awk程序终止。来自后面的打印语句的输出会追加到前面内容的后面。符号">>"用来打开一个文件但是不清空原有文 件的内容,重定向的输出只是被追加到这个文件的末尾。
/> awk '$4 >= 70 {print
$1,$2 > "passing_file"}' filename #注意这里的文件名需要用双引号括起来。
#通过两次cat的结果可以看出>和>>的区别。
/> awk '/north/{print $1,$3,$4 >
"districts" }' testfile
/> cat districts
northwest Joel Craig
northeast TJ Nichols
north Val Shultz
/> awk '/south/{print $1,$3,$4
>> "districts" }' testfile
/> cat districts
northwest Joel Craig
northeast TJ Nichols
north Val Shultz
southwest Chris Foster
southern May Chin
southeast Derek Jonhson
awk中对于输入重定向是通过getline函数来完成的。getline函数的作用是从标准输入、管道或者当前正在处理的文件之外的其他输入文件获得 输入。他负责从输入获得下一行的内容,并给NF、NR和FNR等内建变量赋值。如果得到一个记录,getline就返回1,如果达到文件末尾就返回0。如 果出现错误,如打开文件失败,就返回-1。
/> awk 'BEGIN { "date" |
getline d; print d}'
Tue Nov 15 15:31:42 CST 2011
上例中的BEGIN动作模块中,先执行shell命令date,并通过管道输出给getline,然后再把输出赋值给自定义变量d并打印输出它。
/> awk 'BEGIN { "date" |
getline d; split(d,mon); print mon[2]}'
Nov
上例中date命令通过管道输出给getline并赋值给d变量,再通过内置函数split将d拆分为mon数组,最后print出mon数组的第二个元素。
/> awk 'BEGIN {
while("ls" | getline) print}'
employees
employees2
testfile
命令ls的输出传递给getline作为输入,循环的每个反复,getline都从ls的结果中读取一行输入,并把他打印到屏幕。
/> awk 'BEGIN { printf "What
is your name? "; \
getline name <
"/dev/tty"}\
$1 ~ name {print "Found"
name " on line ", NR "."}\
END {print "See ya, " name
"."}' employees2
What is your name? Mary
Found Mary on line 2.
See ya, Mary.
上例先是打印出BEGIN块中的"What is your name? ",然后等待用户从/dev/tty输入,并将读入的数据赋值给name变量,之后再从输入文件中读取记录,并找到匹配输入变量的记录并打印出来,最后在END块中输出结尾信息。
/> awk 'BEGIN { while(getline <
"/etc/passwd" > 0) lc++; print lc}'
32
awk将逐行读取/etc/passwd文件中的内容,在达到文件末尾之前,计数器lc一直自增1,当到了末尾后打印lc的值。lc的值为/etc/passwd文件的行数。
由于awk中同时打开的管道只有一个,那么在打开下一个管道之前必须关闭它,管道符号右边可以通过可以通过双引号关闭管道。如果不关闭,它将始终保持打开状态,直到awk退出。
/> awk {print $1,$2,$3 |
"sort -4 +1 -2 +0 -1"} END {close("sort -4 +1 -2 +0 -1") }
filename
上例中END模块中的close显示关闭了sort的管道,需要注意的是close中关闭的命令必须和当初打开时的完全匹配,否则END模块产生的输出会和以前的输出一起被sort分类。
3. 条件语句:
awk中的条件语句是从C语言中借鉴来的,见如下声明方式:
if (expression) {
statement;
statement;
... ...
}
/> awk '{if ($6 > 50) print $1 "Too hign"}'
filename
/> awk '{if ($6 > 20 && $6 <= 50) {
safe++; print "OK}}' filename
if (expression) {
statement;
} else {
statement2;
}
/> awk '{if ($6 > 50) print $1 " Too
high"; else print "Range is OK" }' filename
/> awk '{if ($6 > 50) { count++; print $3 } else
{ x = 5; print $5 }' filename
if (expression) {
statement1;
} else if (expression1) {
statement2;
} else {
statement3;
}
/> awk '{if ($6 > 50) print "$6 >
50" else if ($6 > 30) print "$6 > 30" else print
"other"}' filename
4. 循环语句:
awk中的循环语句同样借鉴于C语言,支持while、do/while、for、break、continue,这些关键字的语义和C语言中的语义完全相同。
5. 流程控制语句:
next语句是从文件中读取下一行,然后从头开始执行awk脚本。
exit语句用于结束awk程序。它终止对记录的处理。但是不会略过END模块,如果exit()语句被赋值0--255之间的参数,如exit(1),这个参数就被打印到命令行,以判断退出成功还是失败。
6. 数组:
因 为awk中数组的下标可以是数字和字母,数组的下标通常被称为关键字(key)。值和关键字都存储在内部的一张针对key/value应用hash的表格 里。由于hash不是顺序存储,因此在显示数组内容时会发现,它们并不是按照你预料的顺序显示出来的。数组和变量一样,都是在使用时自动创建的,awk也 同样会自动判断其存储的是数字还是字符串。一般而言,awk中的数组用来从记录中收集信息,可以用于计算总和、统计单词以及跟踪模板被匹配的次数等等。
/> cat employees
Tom Jones
4424 5/12/66
543354
Mary Adams 5346
11/4/63 28765
Sally Chang 1654
7/22/54 650000
Billy Black 1683
9/23/44 336500
/> awk '{name[x++] = $2}; END{for (i = 0; i < NR;
i++) print i, name[i]}' employees
0 Jones
1 Adams
2 Chang
3 Black
在上例中,数组name的下标是变量x。awk初始化该变量的值为0,在每次使用后自增1,读取文件中的第二个域的值被依次赋值给name数组的各个元素。在END模块中,for循环遍历数组的值。因为下标是关键字,所以它不一定从0开始,可以从任何值开始。
#这里是用内置变量NR作为数组的下标了。
/> awk '{id[NR] = $3}; END {for (x = 1; x <= NR;
x++) print id[x]}' employees
4424
5346
1654
1683
awk中还提供了一种special for的循环,见如下声明:
for (item in arrayname) {
print arrayname[item]
}
/> cat db
Tom Jones
Mary Adams
Sally Chang
Billy Black
Tom Savage
Tom Chung
Reggie Steel
Tommy Tucker
/> awk '/^Tom/{name[NR]=$1}; END {for(i = 1;i <=
NR; i++) print name[i]}' db
Tom
Tom
Tom
Tommy
从输出结果可以看出,只有匹配正则表达式的记录的第一个域被赋值给数组name的指定下标元素。因为用NR作为下标,所以数组的下标不可能是连续的,因 此在END模块中用传统的for循环打印时,不存在的元素就打印空字符串了。下面我们看看用special for的方式会有什么样的输出。
/> awk '/^Tom/{name[NR]=$1};END{for(i in name) print
name[i]}' db
Tom
Tom
Tommy
Tom
下面我们看一下用字符串作为下标的例子:(如果下标是字符串文字常量,则需要用双引号括起来)
/> cat testfile2
tom
mary
sean
tom
mary
mary
bob
mary
alex
/> awk '/tom/{count["tom"]++};
/mary/{count["mary"]++}; END{print "There are "
count["tom"] \
" Toms and "
count["mary"] " Marys in the file."} testfile2
There are 2 Toms and 4 Marys in the file.
在上例中,count数组有两个元素,下标分别为tom和mary,每一个元素的初始值都是0,没有tom被匹配的时候,count["tom"]就会加一,count["mary"]在匹配mary的时候也同样如此。END模块中打印出存储在数组中的各个元素。
/> awk '{count[$1]++}; END{for(name in count) printf
"%-5s%d\n",name, count[name]}' testfile2
mary 4
tom 2
alex 1
bob 1
sean 1
在上例中,awk是以记录的域作为数组count的下标。
/> awk '{count[$1]++; if (count[$1] > 1)
name[$1]++}; END{print "The duplicates were "; for(i in name) print
i}' testfile2
The duplicates were
mary
tom
在上例中,如count[$1]的元素值大于1的时候,也就是当名字出现多次的时候,一个新的数组name将被初始化,最后打印出那么数组中重复出现的名字下标。
之前我们介绍的都是如何给数组添加新的元素,并赋予初值,现在我们需要介绍一下如何删除数组中已经存在的元素。要完成这一功能我们需要使用内置函数delete,见如下命令:
/> awk '{count[$1]++}; \
END{for(name in count) {\
if
(count[name] == 1)\
delete count[name];\
} \
for (name in
count) \
print
name}' testfile2
mary
tom
上例中的主要技巧来自END模块,先是变量count数组,如果数组中某个元素的值等于1,则删除该元素,这样等同于删除只出现一次的名字。最后用special for循环打印出数组中仍然存在的元素下标名称。
最后我们来看一下如何使用命令行参数数组,见如下命令:
/> awk 'BEGIN {for(i = 0; i < ARGC; i++)
printf("argv[%d] is %s.\n",i,ARGV[i]); printf("The number of
arguments, ARGC=%d\n",ARGC)}' testfile "Peter Pan" 12
argv[0] is awk.
argv[1] is testfile.
argv[2] is Peter Pan.
argv[3] is 12.
The number of arguments, ARGC=4
从输出结果可以看出,命令行参数数组ARGV是以0作为起始下标的,命令行的第一个参数为命令本身(awk),这个使用方式和C语句main函数完全一致。
/> awk 'BEGIN{name=ARGV[2]; print "ARGV[2] is
" ARGV[2]}; $1 ~ name{print $0}' testfile2 "bob"
ARGV[2] is bob
bob
awk: (FILENAME=testfile2 FNR=9) fatal: cannot open file
`bob' for reading (No such file or directory)
先解释一下以上命令的含义,name变量被赋值为命令行的第三个参数,即bob,之后再在输入文件中找到匹配该变量值的记录,并打印出该记录。
在输出的第二行报出了awk的处理错误信息,这主要是因为awk将bob视为输入文件来处理了,然而事实上这个文件并不存在,下面我们需要做进一步的处理来修正这个问题。
/> awk 'BEGIN{name=ARGV[2]; print "ARGV[2] is
" ARGV[2]; delete ARGV[2]}; $1 ~ name{print $0}' testfile2
"bob"
ARGV[2] is bob
bob
从输出结果中我们可以看到我们得到了我们想要的结果。需要注意的是delete函数的调用必要要在BEGIN模块中完成,因为这时awk还没有开始读取命令行参数中指定的文件。
7. 内建函数:
字符串函数
sub(regular expression,substitution string);
sub(regular expression,substitution string,target
string);
/> awk '{sub("Tom","Tommy");
print}' employees #这里使用Tommy替换了Tom。
Tommy Jones
4424 5/12/66
543354
#当正则表达式Tom在第一个域中第一次被匹配后,他将被字符串"Tommy"替换,如果将sub函数的第三个参数改为$2,将不会有替换发生。
/> awk '{sub("Tom","Tommy",$1);
print}' employees
Tommy Jones
4424 5/12/66
543354
gsub(regular expression,substitution string);
gsub(regular expression,substitution string,target
string);
和sub不同的是,如果第一个参数中正则表达式在记录中出现多次,那么gsub将完成多次替换,而sub只是替换第一次出现的。
index(string,substring)
该函数将返回第二个参数在第一个参数中出现的位置,偏移量从1开始。
/> awk 'BEGIN{print
index("hello","el")}'
2
length(string)
该函数返回字符串的长度。
/> awk 'BEGIN{print length("hello")}'
5
substr(string,starting position)
substr(string,starting position,length of string)
该函数返回第一个参数的子字符串,其截取起始位置为第二个参数(偏移量为1),截取长度为第三个参数,如果没有该参数,则从第二个参数指定的位置起,直到string的末尾。
/> awk 'BEGIN{name = substr("Hello
World",2,3); print name}'
ell
match(string,regular expression)
该函数返回在字符串中正则表达式位置的索引,如果找不到指定的正则表达式就返回0.match函数设置内置变量RSTART为字符串中子字符串的开始位置,RLENGTH为到字字符串末尾的字符个数。
/> awk 'BEGIN{start=match("Good ole
CHINA", /[A-Z]+$/); print start}'
10
上例中的正则表达式[A-Z]+$表示在字符串的末尾搜索连续的大写字母。在字符串"Good ole
CHINA"的第10个位置找到字符串"CHINA"。
/> awk 'BEGIN{start=match("Good ole CHINA",
/[A-Z]+$/); print RSTART, RLENGTH}'
10 5
RSTART表示匹配时的起始索引,RLENGTH表示匹配的长度。
/> awk 'BEGIN{string="Good ole
CHINA";start=match(string, /[A-Z]+$/); print substr(string,RSTART,
RLENGTH)}'
CHINA
这里将match、RSTART、RLENGTH和substr巧妙的结合起来了。
toupper(string)
tolower(string)
以上两个函数分别返回参数字符串的大写和小写的形式。
/> awk 'BEGIN {print toupper("hello");
print tolower("WORLD")}'
HELLO
world
split(string,array,field seperator)
split(string,array)
该函数使用作为第三个参数的域分隔符把字符串分隔为一个数组。如果第三个参数没有提供,则使用当前默认的FS值。
/> awk
'BEGIN{split("11/20/2011",date,"/"); print date[2]}'
20
variable = sprintf("string with format specifiers
",expr1,expr2,...)
该函数和printf的差别等同于C语言中printf和sprintf的差别。前者将格式化后的结果输出到输出流,而后者输出到函数的返回值中。
/> awk 'BEGIN{line = sprintf("%-15s %6.2f
", "hello",4.2); print line}'
hello
4.20
时间函数:
systime()
该函数返回当前时间距离1970年1月1日之间相差的秒数。
/> awk 'BEGIN{print systime()}'
1321369554
strftime()
时间格式化函数,其格式化规则等同于C语言中的strftime函数提供的规则,见以下列表:
数据格式 |
含义 |
%a |
Abbreviated weekday name |
%A |
Full weekday name |
%b |
Abbreviated month name |
%B |
Full month name |
%c |
Date and time representation appropriate for locale |
%d |
Day of month as decimal number (01 – 31) |
%H |
Hour in 24-hour format (00 – 23) |
%I |
Hour in 12-hour format (01 – 12) |
%j |
Day of year as decimal number (001 – 366) |
%m |
Month as decimal number (01 – 12) |
%M |
Minute as decimal number (00 – 59) |
%p |
Current locale's A.M./P.M. indicator for 12-hour clock |
%S |
Second as decimal number (00 – 59) |
%U |
Week of year as decimal number, with Sunday as first day of week (00 – 53) |
%w |
Weekday as decimal number (0 – 6; Sunday is 0) |
%W |
Week of year as decimal number, with Monday as first day of week (00 – 53) |
%x |
Date representation for current locale |
%X |
Time representation for current locale |
%y |
Year without century, as decimal number (00 – 99) |
%Y |
Year with century, as decimal number |
/> awk 'BEGIN{ print strftime("%D",systime())}'
11/15/11
/> awk 'BEGIN{ now = strftime("%T"); print
now}'
23:17:29
内置数学函数:
名称 |
返回值 |
atan2(x,y) |
y,x范围内的余切 |
cos(x) |
余弦函数 |
exp(x) |
求幂 |
int(x) |
取整 |
log(x) |
自然对数 |
sin(x) |
正弦函数 |
sqrt(x) |
平方根 |
/> awk 'BEGIN{print 31/3}'
10.3333
/> awk 'BEGIN{print int(31/3)}'
10
自定义函数:
自定义函数可以放在awk脚本的任何可以放置模板和动作的地方。
function name(parameter1,parameter2,...) {
statements
return expression
}
给函数中本地变量传递值。只使用变量的拷贝。数组通过地址或者指针传递,所以可以在函数内部直接改变数组元素的值。函数内部使用的任何没有作为参数传递 的变量都被看做是全局变量,也就是这些变量对于整个程序都是可见的。如果变量在函数中发生了变化,那么就是在整个程序中发生了改变。唯一向函数提供本地变 量的办法就是把他们放在参数列表中,这些参数通常被放在列表的最后。如果函数调用没有提供正式的参数,那么参数就初始化为空。return语句通常就返回 程序控制并向调用者返回一个值。
/> cat grades
20 10
30 20
40 30
/> cat add.sc
function add(first,second) {
return first
+ second
}
{ print add($1,$2) }
/> awk -f add.sc grades
30
50
70
十二. 行的排序命令sort:
1. sort命令行选项:
选项 |
描述 |
-t |
字段之间的分隔符 |
-f |
基于字符排序时忽略大小写 |
-k |
定义排序的域字段,或者是基于域字段的部分数据进行排序 |
-m |
将已排序的输入文件,合并为一个排序后的输出数据流 |
-n |
以整数类型比较字段 |
-o outfile |
将输出写到指定的文件 |
-r |
倒置排序的顺序为由大到小,正常排序为由小到大 |
-u |
只有唯一的记录,丢弃所有具有相同键值的记录 |
-b |
忽略前面的空格 |
2. sort使用实例:
提示:在下面的输出结果中红色标注的为第一排序字段,后面的依次为紫、绿。
/> sed -n '1,5p' /etc/passwd > users
/> cat users
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
#-t定义了冒号为域字段之间的分隔符,-k 1指定基于第一个字段正向排序(字段顺序从1开始)。
/> sort -t':' -k 1 users
adm:x:3:4:adm:/var/adm:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
root:x:0:0:root:/root:/bin/bash
#还是以冒号为分隔符,这次是基于第三个域字段进行倒置排序。
/> sort -t':' -k 3r users
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
root:x:0:0:root:/root:/bin/bash
#先以第六个域的第2个字符到第4个字符进行正向排序,再基于第一个域进行反向排序。
/> sort -t':' -k
6.2,6.4 -k 1r users
bin:x:1:1:bin:/bin:/sbin/nologin
root:x:0:0:root:/root:/bin/bash
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
#先以第六个域的第2个字符到第4个字符进行正向排序,再基于第一个域进行正向排序。和上一个例子比,第4和第5行交换了位置。
/> sort -t':' -k 6.2,6.4 -k 1 users
bin:x:1:1:bin:/bin:/sbin/nologin
root:x:0:0:root:/root:/bin/bash
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
#基于第一个域的第2个字符排序
/> sort -t':' -k 1.2,1.2 users
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
root:x:0:0:root:/root:/bin/bash
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
#基于第六个域的第2个字符到第4个字符进行正向排序,-u命令要求在排序时删除键值重复的行。
/> sort -t':' -k
6.2,6.4 -u users
bin:x:1:1:bin:/bin:/sbin/nologin
root:x:0:0:root:/root:/bin/bash
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
/> cat /etc/passwd
| wc -l #计算该文件中文本的行数。
39
/> sed -n '35,$p'
/etc/passwd > users2 #取最后5行并输出到users2中。
/> cat users2
sshd:x:74:74:Privilege-separated
SSH:/var/empty/sshd:/sbin/nologin
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/bash
pulse:x:496:494:PulseAudio System
Daemon:/var/run/pulse:/sbin/nologin
gdm:x:42:42::/var/lib/gdm:/sbin/nologin
stephen:x:500:500:stephen:/home/stephen:/bin/bash
#基于第3个域字段以文本的形式排序
/> sort -t':' -k 3
users2
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/bash
gdm:x:42:42::/var/lib/gdm:/sbin/nologin
pulse:x:496:494:PulseAudio System Daemon:/var/run/pulse:/sbin/nologin
stephen:x:500:500:stephen:/home/stephen:/bin/bash
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
#基于第3个域字段以数字的形式排序
/> sort -t':' -k 3n
users2
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/bash
gdm:x:42:42::/var/lib/gdm:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
pulse:x:496:494:PulseAudio System Daemon:/var/run/pulse:/sbin/nologin
stephen:x:500:500:stephen:/home/stephen:/bin/bash
#基于当前系统执行进程的owner名排序,并将排序的结果写入到result文件中
/> ps -ef | sort -k
1 -o result
十三. 删除重复行的命令uniq:
uniq有3个最为常用的选项,见如下列表:
选项 |
命令描述 |
-c |
可在每个输出行之前加上该行重复的次数 |
-d |
仅显示重复的行 |
-u |
显示没有重复的行 |
/> cat testfile
hello
world
friend
hello
world
hello
#直接删除未经排序的文件,将会发现没有任何行被删除
/> uniq testfile
hello
world
friend
hello
world
hello
#排序之后删除了重复行,同时在行首位置输出该行重复的次数
/> sort testfile | uniq -c
1 friend
3 hello
2 world
#仅显示存在重复的行,并在行首显示该行重复的次数
/> sort testfile | uniq -dc
3 hello
2 world
#仅显示没有重复的行
/> sort testfile | uniq -u
friend
十四. 文件压缩解压命令tar:
1. tar命令行选项
选项 |
命令描述 |
-c |
建立压缩档案 |
-x |
解压 |
--delete |
从压缩包中删除已有文件,如果该文件在包中出现多次,该操作将其全部删除。 |
-t |
查看压缩包中的文件列表 |
-r |
向压缩归档文件末尾追加文件 |
-u |
更新原压缩包中的文件 |
-z |
压缩为gzip格式,或以gzip格式解压 |
-j |
压缩为bzip2格式,或以bzip2格式解压 |
-v |
显示压缩或解压的过程,该选项一般不适于后台操作 |
-f |
使用档案名字,这个参数是最后一个参数,后面只能接档案名。 |
2. tar使用实例:
#将当前目录下所有文件压缩打包,需要说明的是很多人都习惯将tar工具压缩的文件的扩展名命名为.tar
/> tar -cvf test.tar *
-rw-r--r--. 1 root root 183 Nov 11 08:02 users
-rw-r--r--. 1 root root 279 Nov 11 08:45 users2
/> cp ../*.log .
#从上一层目录新copy一个.log文件到当前目录。
/> tar -rvf test.tar *.log #将扩展名为.log的文件追加到test.tar包里。
/> tar -tvf test.tar
-rw-r--r--
root/root 183 2011-11-11 08:02 users
-rw-r--r--
root/root 279 2011-11-11 08:45 users2
-rw-r--r-- root/root 48217
2011-11-11 22:16 install.log
/> touch install.log #使原有的文件更新一下最新修改时间
/> tar -uvf test.tar *.log #重新将更新后的log文件更新到test.tar中
/> tar -tvf test.tar #从输出结果可以看出tar包中多出一个更新后install.log文件。
-rw-r--r--
root/root 183 2011-11-11 08:02
users
-rw-r--r--
root/root 279 2011-11-11 08:45
users2
-rw-r--r-- root/root 48217
2011-11-11 22:16 install.log
-rw-r--r-- root/root 48217
2011-11-11 22:20 install.log
/> tar --delete install.log -f test.tar #基于上面的结果,从压缩包中删除install.log
-rw-r--r-- root/root 183
2011-11-11 08:02 users
-rw-r--r-- root/root 279
2011-11-11 08:45 users2
/> rm -f users users2 #从当前目录将tar中的两个文件删除
/> tar -xvf test.tar #解压
/> ls -l users* #仅列出users和users2的详细列表信息
-rw-r--r--. 1 root root 183 Nov 11 08:02 users
-rw-r--r--. 1 root root 279 Nov 11 08:45 users2
#以gzip的格式压缩并打包,解压时也应该以同样的格式解压,需要说明的是以该格式压缩的包习惯在扩展名后加.gz
/> tar -cvzf test.tar.gz *
/> tar -tzvf
test.tar.gz #查看压缩包中文件列表时也要加z选项(gzip格式)
-rw-r--r-- root/root 48217
2011-11-11 22:50 install.log
-rw-r--r--
root/root 183 2011-11-11 08:02
users
-rw-r--r--
root/root 279 2011-11-11 08:45
users2
/> rm -f users users2 install.log
/> tar -xzvf test.tar.gz #以gzip的格式解压
/> ls -l *.log users*
-rw-r--r-- root/root 48217
2011-11-11 22:50 install.log
-rw-r--r--
root/root 183 2011-11-11 08:02
users
-rw-r--r--
root/root 279 2011-11-11 08:45
users2
/> rm -f
test.*
#删除当前目录下原有的压缩包文件
#以bzip2的格式压缩并打包,解压时也应该以同样的格式解压,需要说明的是以该格式压缩的包习惯在扩展名后加.bz2
/> tar -cvjf test.tar.bz2 *
/> tar -tjvf test.tar.bz2 #查看压缩包中文件列表时也要加j选项(bzip2格式)
-rw-r--r-- root/root 48217
2011-11-11 22:50 install.log
-rw-r--r--
root/root 183 2011-11-11 08:02
users
-rw-r--r-- root/root
279 2011-11-11 08:45 users2
/> rm -f *.log user*
/> tar -xjvf test.tar.bz2 #以bzip2的格式解压
/> ls -l
-rw-r--r--. 1 root root 48217 Nov 11 22:50 install.log
-rw-r--r--. 1 root root 10530 Nov 11 23:08 test.tar.bz2
-rw-r--r--. 1 root root 183 Nov 11
08:02 users
-rw-r--r--. 1 root root 279 Nov 11
08:45 users2
十五. 大文件拆分命令split:
下面的列表中给出了该命令最为常用的几个命令行选项:
选项 |
描述 |
-l |
指定行数,每多少分隔成一个文件,缺省值为1000行。 |
-b |
指定字节数,支持的单位为:k和m |
-C |
与-b参数类似,但切割时尽量维持每行的完整性 |
-d |
生成文件的后缀为数字,如果不指定该选项,缺省为字母 |
/> ls -l
-rw-r--r--. 1 root root 10530 Nov 11 23:08 test.tar.bz2
/> split -b 5k test.tar.bz2 #以每文件5k的大小切割test.tar.bz2
/> ls -l
#查看切割后的结果,缺省情况下拆分后的文件名为以下形式。
-rw-r--r--. 1 root root 10530 Nov 11 23:08 test.tar.bz2
-rw-r--r--. 1 root root 5120 Nov 11 23:34 xaa
-rw-r--r--. 1 root root 5120 Nov 11 23:34 xab
-rw-r--r--. 1 root root 290 Nov 11 23:34
xac
/> rm -f x*
#删除拆分后的小文件
/> split -d -b 5k test.tar.bz2 #-d选项以后缀为数字的形式命名拆分后的小文件
/> ls -l
-rw-r--r--. 1 root root 10530 Nov 11 23:08 test.tar.bz2
-rw-r--r--. 1 root root 5120 Nov 11 23:36 x00
-rw-r--r--. 1 root root 5120 Nov 11 23:36 x01
-rw-r--r--. 1 root root 290 Nov 11 23:36
x02
/> wc install.log
-l
#计算该文件的行数
/> split -l 300 install.log #每300行拆分成一个小文件
/> ls -l x*
-rw-r--r--. 1 root root 11184 Nov 11 23:42 xaa
-rw-r--r--. 1 root root 10805 Nov 11 23:42 xab
-rw-r--r--. 1 root root 12340 Nov 11 23:42 xac
-rw-r--r--. 1 root root 11783 Nov 11 23:42 xad
-rw-r--r--. 1 root root 2105 Nov 11 23:42 xae
十六. 文件查找命令find:
下面给出find命令的主要应用示例:
/> ls -l
#列出当前目录下所包含的测试文件
-rw-r--r--. 1 root root 48217 Nov 12 00:57 install.log
-rw-r--r--. 1 root root 37 Nov
12 00:56 testfile.dat
-rw-r--r--. 1 root root 10530 Nov 11 23:08 test.tar.bz2
-rw-r--r--. 1 root root 183 Nov 11
08:02 users
-rw-r--r--. 1 root root 279 Nov 11
08:45 users2
1. 按文件名查找:
-name: 查找时文件名大小写敏感。
-iname: 查找时文件名大小写不敏感。
#该命令为find命令中最为常用的命令,即从当前目录中查找扩展名为.log的文件。需要说明的是,缺省情况下,find会从指定的目录搜索,并递归的搜索其子目录。
/> find . -name "*.log"
./install.log
/> find . -iname
U*
#如果执行find . -name U*将不会找到匹配的文件
users users2
2. 按文件时间属性查找:
-atime -n[+n]: 找出文件访问时间在n日之内[之外]的文件。
-ctime -n[+n]: 找出文件更改时间在n日之内[之外]的文件。
-mtime -n[+n]: 找出修改数据时间在n日之内[之外]的文件。
-amin -n[+n]: 找出文件访问时间在n分钟之内[之外]的文件。
-cmin -n[+n]: 找出文件更改时间在n分钟之内[之外]的文件。
-mmin -n[+n]: 找出修改数据时间在n分钟之内[之外]的文件。
/> find -ctime -2 #找出距此时2天之内创建的文件
.
./users2
./install.log
./testfile.dat
./users
./test.tar.bz2
/> find -ctime +2 #找出距此时2天之前创建的文件
没有找到
#因为当前目录下所有文件都是2天之内创建的
/> touch
install.log #手工更新install.log的最后访问时间,以便下面的find命令可以找出该文件
/> find . -cmin
-3 #找出修改状态时间在3分钟之内的文件。
install.log
3. 基于找到的文件执行指定的操作:
-exec: 对匹配的文件执行该参数所给出的shell命令。相应命令的形式为'command' {} \;,注意{}和\;之间的空格,同时两个{}之间没有空格
-ok: 其主要功能和语法格式与-exec完全相同,唯一的差别是在于该选项更加安全,因为它会在每次执行shell命令之前均予以提示,只有在回答为y的时候, 其后的shell命令才会被继续执行。需要说明的是,该选项不适用于自动化脚本,因为该提供可能会挂起整个自动化流程。
#找出距此时2天之内创建的文件,同时基于find的结果,应用-exec之后的命令,即ls -l,从而可以直接显示出find找到文件的明显列表。
/> find . -ctime -2 -exec ls -l {}
\;
-rw-r--r--. 1 root root 279
Nov 11 08:45 ./users2
-rw-r--r--. 1 root root 48217 Nov 12 00:57
./install.log
-rw-r--r--. 1 root
root 37 Nov 12 00:56 ./testfile.dat
-rw-r--r--. 1 root root 183
Nov 11 08:02 ./users
-rw-r--r--. 1 root root 10530 Nov 11 23:08
./test.tar.bz2
#找到文件名为*.log, 同时文件数据修改时间距此时为1天之内的文件。如果找到就删除他们。有的时候,这样的写法由于是在找到之后立刻删除,因此存在一定误删除的危险。
/> ls
install.log testfile.dat test.tar.bz2
users users2
/> find . -name
"*.log" -mtime -1 -exec rm -f {} \;
/> ls
testfile.dat test.tar.bz2 users users2
在控制台下,为了使上面的命令更加安全,我们可以使用-ok替换-exec,见如下示例:
/> find . -name
"*.dat" -mtime -1 -ok rm -f {} \;
< rm ... ./testfile.dat > ? y #对于该提示,如果回答y,找到的*.dat文件将被删除,这一点从下面的ls命令的结果可以看出。
/> ls
test.tar.bz2 users users2
4. 按文件所属的owner和group查找:
-user:
查找owner属于-user选项后面指定用户的文件。
! -user: 查找owner不属于-user选项后面指定用户的文件。
-group: 查找group属于-group选项后面指定组的文件。
! -group: 查找group不属于-group选项后面指定组的文件。
/> ls
-l
#下面三个文件的owner均为root
-rw-r--r--. 1 root root 10530 Nov 11 23:08 test.tar.bz2
-rw-r--r--. 1 root root 183 Nov 11 08:02 users
-rw-r--r--. 1 root root 279 Nov 11 08:45 users2
/> chown stephen users #将users文件的owner从root改为stephen。
/> ls -l
-rw-r--r--. 1 root root 10530 Nov 11 23:08 test.tar.bz2
-rw-r--r--. 1 stephen root 183 Nov 11 08:02 users
-rw-r--r--. 1 root root 279 Nov 11 08:45 users2
/> find . -user root #搜索owner是root的文件
.
./users2
./test.tar.bz2
/> find . ! -user root #搜索owner不是root的文件,注意!和-user之间要有空格。
./users
/> ls
-l
#下面三个文件的所属组均为root
-rw-r--r--. 1 root root 10530 Nov 11 23:08 test.tar.bz2
-rw-r--r--. 1 stephen root 183 Nov 11 08:02 users
-rw-r--r--. 1 root root 279 Nov 11 08:45 users2
/> chgrp stephen users #将users文件的所属组从root改为stephen
/> ls -l
-rw-r--r--. 1
root root 10530 Nov 11 23:08 test.tar.bz2
-rw-r--r--. 1 stephen stephen 183 Nov 11 08:02 users
-rw-r--r--. 1
root root 279 Nov 11
08:45 users2
/> find . -group root #搜索所属组是root的文件
.
./users2
./test.tar.bz2
/> find . ! -group root #搜索所属组不是root的文件,注意!和-user之间要有空格。
./users
5. 按指定目录深度查找:
-maxdepth: 后面的参数表示距当前目录指定的深度,其中1表示当前目录,2表示一级子目录,以此类推。在指定该选项后,find只是在找到指定深度后就不在递归其子目录了。下例中的深度为1,表示只是在当前子目录中搜索。如果没有设置该选项,find将递归当前目录下的所有子目录。
/> mkdir subdir #创建一个子目录,并在该子目录内创建一个文件
/> cd subdir
/> touch testfile
/> cd ..
#maxdepth后面的参数表示距当前目录指定的深度,其中1表示当前目录,2表示一级子目录,以此类推。在指定该选项后,find只是在找到指定深度后就不在递归其子目录了。下例中的深度为1,表示只是在当前子目录中搜索。如果没有设置该选项,find将递归当前目录下的所有子目录。
/> find . -maxdepth 1 -name "*"
.
./users2
./subdir
./users
./test.tar.bz2
#搜索深度为子一级子目录,这里可以看出子目录下刚刚创建的testfile已经被找到
/> find . -maxdepth 2 -name "*"
.
./users2
./subdir
./subdir/testfile
./users
./test.tar.bz2
6. 排除指定子目录查找:
-path pathname -prune: 避开指定子目录pathname查找。
-path expression -prune: 避开表达中指定的一组pathname查找。
需要说明的是,如果同时使用-depth选项,那么-prune将被find命令忽略。
#为后面的示例创建需要避开的和不需要避开的子目录,并在这些子目录内均创建符合查找规则的文件。
/> mkdir DontSearchPath
/> cd DontSearchPath
/> touch datafile1
/> cd ..
/> mkdir DoSearchPath
/> cd DoSearchPath
/> touch datafile2
/> cd ..
/> touch datafile3
#当前目录下,避开DontSearchPath子目录,搜索所有文件名为datafile*的文件。
/> find . -path "./DontSearchPath" -prune
-o -name "datafile*" -print
./DoSearchPath/datafile2
./datafile3
#当前目录下,同时避开DontSearchPath和DoSearchPath两个子目录,搜索所有文件名为datafile*的文件。
/> find . \( -path "./DontSearchPath" -o
-path "./DoSearchPath" \) -prune -o -name "datafile*"
-print
./datafile3
7. 按文件权限属性查找:
-perm mode: 文件权限正好符合mode(mode为文件权限的八进制表示)。
-perm +mode: 文件权限部分符合mode。如命令参数为644(-rw-r--r--),那么只要文件权限属性中有任何权限和644重叠,这样的文件均可以被选出。
-perm -mode: 文件权限完全符合mode。如命令参数为644(-rw-r--r--),当644中指定的权限已经被当前文件完全拥有,同时该文件还拥有额外的权限属性,这样的文件可被选出。
/> ls -l
-rw-r--r--. 1
root
root 0 Nov 12 10:02
datafile3
-rw-r--r--. 1
root
root 10530 Nov 11 23:08 test.tar.bz2
-rw-r--r--. 1 stephen
stephen 183 Nov 11 08:02 users
-rw-r--r--. 1
root
root 279 Nov 11 08:45 users2
/> find . -perm 644 #查找所有文件权限正好为644(-rw-r--r--)的文件。
./users2
./datafile3
./users
./test.tar.bz2
/> find . -perm 444 #当前目录下没有文件的权限属于等于444(均为644)。
/> find . -perm -444 #644所包含的权限完全覆盖444所表示的权限。
.
./users2
./datafile3
./users
./test.tar.bz2
/> find . -perm +111
#查找所有可执行的文件,该命令没有找到任何文件。
/> chmod u+x users #改变users文件的权限,添加owner的可执行权限,以便于下面的命令可以将其找出。
/> find . -perm +111
.
./users
8. 按文件类型查找:
-type:后面指定文件的类型。
b - 块设备文件。
d - 目录。
c - 字符设备文件。
p - 管道文件。
l - 符号链接文件。
f - 普通文件。
/> mkdir subdir
/> find . -type
d #在当前目录下,找出文件类型为目录的文件。
./subdir
/> find . !
-type d #在当前目录下,找出文件类型不为目录的文件。
./users2
./datafile3
./users
./test.tar.bz2
/> find . -type f #在当前目录下,找出文件类型为文件的文件
./users2
./datafile3
./users
./test.tar.bz2
9. 按文件大小查找:
-size [+/-]100[c/k/M/G]: 表示文件的长度为等于[大于/小于]100块[字节/k/M/G]的文件。
-empty: 查找空文件。
/> find . -size +4k -exec ls -l {}
\; #查找文件大小大于4k的文件,同时打印出找到文件的明细
-rw-r--r--. 1 root root 10530 Nov 11 23:08 ./test.tar.bz2
/> find . -size -4k -exec ls -l {}
\; #查找文件大小小于4k的文件。
-rw-r--r--. 1 root
root 279 Nov 11 08:45
./users2
-rw-r--r--. 1
root
root 0 Nov 12 10:02 ./datafile3
-rwxr--r--. 1 stephen stephen 183 Nov 11 08:02 ./users
/> find . -size 183c -exec ls -l {} \; #查找文件大小等于183字节的文件。
-rwxr--r--. 1 stephen stephen 183 Nov 11 08:02 ./users
/> find . -empty -type f -exec ls
-l {} \;
-rw-r--r--. 1 root root 0 Nov 12 10:02 ./datafile3
10. 按更改时间比指定文件新或比文件旧的方式查找:
-newer file1 ! file2: 查找文件的更改日期比file1新,但是比file2老的文件。
/> ls -lrt #以时间顺序(从早到晚)列出当前目录下所有文件的明细列表,以供后面的例子参考。
-rwxr--r--. 1 stephen stephen 183 Nov 11 08:02 users1
-rw-r--r--. 1
root
root 279 Nov 11 08:45 users2
-rw-r--r--. 1
root root
10530 Nov 11 23:08 test.tar.bz2
-rw-r--r--. 1 root
root 0 Nov 12 10:02 datafile3
/> find . -newer
users1 #查找文件更改日期比users1新的文件,从上面结果可以看出,其余文件均符合要求。
./users2
./datafile3
./test.tar.bz2
/> find . ! -newer users2 #查找文件更改日期不比users1新的文件。
./users2
./users
#查找文件更改日期比users2新,但是不比test.tar.bz2新的文件。
/> find . -newer users2 ! -newer
test.tar.bz2
./test.tar.bz2
十七. xargs命令:
该命令的主要功能是从输入中构建和执行shell命令。
在使用find命令的-exec选项处理匹配到的文件时, find命令将所有匹配到的文件一起传递给exec执行。但有些系统对能够传递给exec的命令长度有限制,这样在find命令运行几分钟之后,就会出现溢出错误。错误信息通常是“参数列太长”或“参数列溢出”。这就是xargs命令的用处所在,特别是与find命令一起使用。
find命令把匹配到的文件传递给xargs命令,而xargs命令每次只获取一部分文件而不是全部,不像-exec选项那样。这样它可以先处理最先获取的一部分文件,然后是下一批,并如此继续下去。
在有些系统中,使用-exec选项会为处理每一个匹配到的文件而发起一个相应的进程,并非将匹配到的文件全部作为参数一次执行;这样在有些情况下就会出现进程过多,系统性能下降的问题,因而效率不高;
而使用xargs命令则只有一个进程。另外,在使用xargs命令时,究竟是一次获取所有的参数,还是分批取得参数,以及每一次获取参数的数目都会根据该命令的选项及系统内核中相应的可调参数来确定。
/> ls -l
-rw-r--r--. 1 root root
0 Nov 12 10:02 datafile3
-rw-r--r--. 1 root root 10530 Nov 11 23:08 test.tar.bz2
-rwxr--r--. 1 root root 183 Nov 11
08:02 users
-rw-r--r--. 1 root root 279 Nov 11 08:45
users2
#查找当前目录下的每一个普通文件,然后使用xargs命令来测试它们分别属于哪类文件。
/> find . -type f -print | xargs file
./users2: ASCII
text
./datafile3: empty
./users:
ASCII text
./test.tar.bz2: bzip2 compressed data, block size = 900k
#回收当前目录下所有普通文件的执行权限。
/> find . -type f -print | xargs
chmod a-x
/> ls -l
-rw-r--r--. 1 root root 0 Nov 12
10:02 datafile3
-rw-r--r--. 1 root root 10530 Nov 11 23:08 test.tar.bz2
-rw-r--r--. 1 root root 183 Nov 11 08:02 users
-rw-r--r--. 1 root root 279 Nov 11 08:45 users2
#在当面目录下查找所有普通文件,并用grep命令在搜索到的文件中查找hostname这个词
/> find . -type f -print | xargs grep
"hostname"
#在整个系统中查找内存信息转储文件(core dump) ,然后把结果保存到/tmp/core.log 文件中。
/> find / -name "core"
-print | xargs echo "" >/tmp/core.log
/> pgrep mysql | xargs kill -9 #直接杀掉mysql的进程
[1]+
Killed
mysql
十八. 和系统运行状况相关的Shell命令:
1. Linux的实时监测命令(watch):
watch 是一个非常实用的命令,可以帮你实时监测一个命令的运行结果,省得一遍又一遍的手动运行。该命令最为常用的两个选项是-d和-n,其中-n表示间隔多少秒 执行一次"command",-d表示高亮发生变化的位置。下面列举几个在watch中常用的实时监视命令:
/> watch -d -n 1 'who' #每隔一秒执行一次who命令,以监视服务器当前用户登录的状况
Every 1.0s: who Sat Nov
12 12:37:18 2011
stephen
tty1 2011-11-11
17:38 (:0)
stephen
pts/0 2011-11-11 17:39 (:0.0)
root
pts/1 2011-11-12 10:01
(192.168.149.1)
root
pts/2 2011-11-12 11:41
(192.168.149.1)
root
pts/3 2011-11-12 12:11
(192.168.149.1)
stephen
pts/4 2011-11-12 12:22 (:0.0)
此时通过其他Linux客户端工具以root的身份登录当前Linux服务器,再观察watch命令的运行变化。
Every 1.0s: who Sat Nov
12 12:41:09 2011
stephen
tty1 2011-11-11 17:38
(:0)
stephen
pts/0 2011-11-11 17:39 (:0.0)
root
pts/1 2011-11-12 10:01
(192.168.149.1)
root
pts/2 2011-11-12 11:41
(192.168.149.1)
root
pts/3 2011-11-12 12:40
(192.168.149.1)
stephen
pts/4 2011-11-12 12:22 (:0.0)
root
pts/5 2011-11-12 12:41
(192.168.149.1)
最后一行中被高亮的用户为新登录的root用户。此时按CTRL + C可以退出正在执行的watch监控进程。
#watch可以同时运行多个命令,命令间用分号分隔。
#以下命令监控磁盘的使用状况,以及当前目录下文件的变化状况,包括文件的新增、删除和文件修改日期的更新等。
/> watch -d -n 1 'df -h; ls -l'
Every 1.0s: df -h; ls -l Sat Nov 12
12:55:00 2011
Filesystem
Size Used Avail Use% Mounted on
/dev/sda1
5.8G 3.3G 2.2G 61% /
tmpfs
504M 420K 504M 1% /dev/shm
total 20
-rw-r--r--. 1 root root 10530 Nov 11 23:08 test.tar.bz2
-rw-r--r--. 1 root root 183 Nov 11 08:02 users
-rw-r--r--. 1 root root 279 Nov 11 08:45 users2
此时通过另一个Linux控制台窗口,在watch监视的目录下,如/home/stephen/test,执行下面的命令
/> touch aa
#在执行该命令之后,另一个执行watch命令的控制台将有如下变化
Every 1.0s: df -h; ls
-l
Sat Nov 12 12:57:08 2011
Filesystem
Size Used Avail Use% Mounted on
/dev/sda1
5.8G 3.3G 2.2G 61% /
tmpfs
504M 420K 504M 1% /dev/shm
total 20
-rw-r--r--. 1 root
root 0 Nov 12 12:56 aa
-rw-r--r--. 1 root
root 0 Nov 12 10:02 datafile3
-rw-r--r--. 1 root root 10530 Nov 11 23:08 test.tar.bz2
-rw-r--r--. 1 root root 183 Nov 11
08:02 users
-rw-r--r--. 1 root root 279 Nov 11
08:45 users2
其中黄色高亮的部分,为touch aa命令执行之后watch输出的高亮变化部分。
2. 查看当前系统内存使用状况(free):
free命令有以下几个常用选项:
选项 |
说明 |
-b |
以字节为单位显示数据。 |
-k |
以千字节(KB)为单位显示数据(缺省值)。 |
-m |
以兆(MB)为单位显示数据。 |
-s delay |
该选项将使free持续不断的刷新,每次刷新之间的间隔为delay指定的秒数,如果含有小数点,将精确到毫秒,如0.5为500毫秒,1为一秒。 |
free命令输出的表格中包含以下几列:
列名 |
说明 |
total |
总计物理内存的大小。 |
used |
已使用的内存数量。 |
free |
可用的内存数量。 |
Shared |
多个进程共享的内存总额。 |
Buffers/cached |
磁盘缓存的大小。 |
见以下具体示例和输出说明:
/> free -k
total
used
free shared
buffers cached
Mem: 1031320
671776
359544
0 88796 352564
-/+
buffers/cache: 230416
800904
Swap:
204792
0 204792
对于free命令的输出,我们只需关注红色高亮的输出行和绿色高亮的输出行,见如下具体解释:
红色输出行:该行是从操作系统的角度来看待输出数据的,used(671776)表示内核(Kernel)+Applications+buffers+cached。free(359544)表示系统还有多少内存可供使用。
绿色输出行:该行则是从应用程序的角度来看输出数据的。其free = 操作系统used + buffers +
cached,既:
800904 = 359544 + 88796 + 352564
/> free -m
total
used
free shared
buffers cached
Mem:
1007
656
351 0
86 344
-/+ buffers/cache:
225 782
Swap:
199
0 199
/> free -k -s 1.5 #以千字节(KB)为单位显示数据,同时每隔1.5刷新输出一次,直到按CTRL+C退出
total
used free
shared buffers cached
Mem:
1007
655
351 0
86 344
-/+ buffers/cache:
224 782
Swap:
199
0 199
total
used free
shared buffers cached
Mem:
1007
655
351 0
86 344
-/+ buffers/cache:
224 782
Swap:
199
0 199
3. CPU的实时监控工具(mpstat):
该命令主要用于报告当前系统中所有CPU的实时运行状况。
#该命令将每隔2秒输出一次CPU的当前运行状况信息,一共输出5次,如果没有第二个数字参数,mpstat将每隔两秒执行一次,直到按CTRL+C退出。
/> mpstat 2 5
Linux 2.6.32-71.el6.i686 (Stephen-PC)
11/12/2011 _i686_
(1 CPU)
04:03:00 PM CPU %usr
%nice %sys %iowait %irq
%soft %steal %guest %idle
04:03:02 PM all
0.00 0.00 0.50
0.00 0.00 0.00
0.00 0.00 99.50
04:03:04 PM all
0.00 0.00 0.00
0.00 0.00 0.00
0.00 0.00 100.00
04:03:06 PM all
0.00 0.00 0.00
0.00 0.00 0.00
0.00 0.00 100.00
04:03:08 PM all
0.00 0.00 0.00
0.00 0.00 0.00 0.00
0.00 100.00
04:03:10 PM all
0.00 0.00 0.00
0.00 0.00 0.00
0.00 0.00 100.00
Average:
all 0.00 0.00
0.10 0.00 0.00
0.00 0.00 0.00 99.90
第一行的末尾给出了当前系统中CPU的数量。后面的表格中则输出了系统当前CPU的使用状况,以下为每列的含义:
列名 |
说明 |
%user |
在internal时间段里,用户态的CPU时间(%),不包含nice值为负进程 (usr/total)*100 |
%nice |
在internal时间段里,nice值为负进程的CPU时间(%) (nice/total)*100 |
%sys |
在internal时间段里,内核时间(%) (system/total)*100 |
%iowait |
在internal时间段里,硬盘IO等待时间(%) (iowait/total)*100 |
%irq |
在internal时间段里,硬中断时间(%) (irq/total)*100 |
%soft |
在internal时间段里,软中断时间(%) (softirq/total)*100 |
%idle |
在internal时间段里,CPU除去等待磁盘IO操作外的因为任何原因而空闲的时间闲置时间(%) (idle/total)*100 |
计算公式:
total_cur=user+system+nice+idle+iowait+irq+softirq
total_pre=pre_user+ pre_system+ pre_nice+ pre_idle+
pre_iowait+ pre_irq+ pre_softirq
user=user_cur – user_pre
total=total_cur-total_pre
其中_cur 表示当前值,_pre表示interval时间前的值。上表中的所有值可取到两位小数点。
/> mpstat -P ALL 2 3 #-P ALL表示打印所有CPU的数据,这里也可以打印指定编号的CPU数据,如-P 0(CPU的编号是0开始的)
Linux 2.6.32-71.el6.i686 (Stephen-PC)
11/12/2011 _i686_ (1 CPU)
04:12:54 PM CPU %usr
%nice %sys %iowait %irq
%soft %steal %guest %idle
04:12:56 PM
all 0.00
0.00 0.50
0.00 0.00
0.00 0.00
0.00 99.50
04:12:56 PM
0 0.00
0.00 0.50
0.00 0.00
0.00 0.00
0.00 99.50
04:12:56 PM CPU %usr
%nice %sys %iowait %irq
%soft %steal %guest %idle
04:12:58 PM all
0.00 0.00
0.00 0.00
0.00 0.00
0.00 0.00 100.00
04:12:58 PM
0 0.00
0.00 0.00
0.00 0.00
0.00 0.00
0.00 100.00
04:12:58 PM CPU %usr
%nice %sys %iowait %irq
%soft %steal %guest %idle
04:13:00 PM
all 0.00
0.00 0.00
0.00 0.00
0.00 0.00 0.00
100.00
04:13:00 PM
0 0.00 0.00
0.00 0.00
0.00 0.00 0.00
0.00 100.00
Average: CPU
%usr %nice %sys %iowait
%irq %soft %steal %guest %idle
Average:
all 0.00
0.00 0.17 0.00
0.00 0.00 0.00
0.00 99.83
Average:
0 0.00
0.00 0.17 0.00
0.00 0.00 0.00
0.00 99.83
4. 虚拟内存的实时监控工具(vmstat):
vmstat命令用来获得UNIX系统有关进程、虚存、页面交换空间及CPU活动的信息。这些信息反映了系统的负载情况。vmstat首次运行时显示自系统启动开始的各项统计信息,之后运行vmstat将显示自上次运行该命令以后的统计信息。用户可以通过指定统计的次数和时间来获得所需的统计信息。
/> vmstat 1 3 #这是vmstat最为常用的方式,其含义为每隔1秒输出一条,一共输出3条后程序退出。
procs -----------memory----------
---swap-- -----io---- --system-- -----cpu-----
r b swpd
free buff cache
si so bi
bo in cs us sy id wa st
0 0 0
531760 67284 231212 108 0 0
260 111 148 1 5 86 8 0
0 0 0
531752 67284 231212 0
0 0 0
33 57 0 1 99 0 0
0 0 0
531752 67284 231212 0
0 0 0
40 73 0 0 100 0 0
/> vmstat 1 #其含义为每隔1秒输出一条,直到按CTRL+C后退出。
下面将给出输出表格中每一列的含义说明:
有关进程的信息有:(procs)
r: 在就绪状态等待的进程数。
b: 在等待状态等待的进程数。
有关内存的信息有:(memory)
swpd: 正在使用的swap大小,单位为KB。
free: 空闲的内存空间。
buff: 已使用的buff大小,对块设备的读写进行缓冲。
cache: 已使用的cache大小,文件系统的cache。
有关页面交换空间的信息有:(swap)
si: 交换内存使用,由磁盘调入内存。
so: 交换内存使用,由内存调入磁盘。
有关IO块设备的信息有:(io)
bi: 从块设备读入的数据总量(读磁盘) (KB/s)
bo: 写入到块设备的数据总理(写磁盘)
(KB/s)
有关故障的信息有:(system)
in: 在指定时间内的每秒中断次数。
sy: 在指定时间内每秒系统调用次数。
cs: 在指定时间内每秒上下文切换的次数。
有关CPU的信息有:(cpu)
us: 在指定时间间隔内CPU在用户态的利用率。
sy: 在指定时间间隔内CPU在核心态的利用率。
id: 在指定时间间隔内CPU空闲时间比。
wa: 在指定时间间隔内CPU因为等待I/O而空闲的时间比。
vmstat 可以用来确定一个系统的工作是受限于CPU还是受限于内存:如果CPU的sy和us值相加的百分比接近100%,或者运行队列(r)中等待的进程数总是不等于0,且经常大于4,同时id也经常小于40,则该系统受限于CPU;如果bi、bo的值总是不等于0,则该系统受限于内存。
5. 设备IO负载的实时监控工具(iostat):
iostat主要用于监控系统设备的IO负载情况,iostat首次运行时显示自系统启动开始的各项统计信息,之后运行iostat将显示自上次运行该命令以后的统计信息。用户可以通过指定统计的次数和时间来获得所需的统计信息。
其中该命令中最为常用的使用方式如下:
/> iostat -d 1 3 #仅显示设备的IO负载,其中每隔1秒刷新并输出结果一次,输出3次后iostat退出。
Linux 2.6.32-71.el6.i686 (Stephen-PC)
11/16/2011 _i686_ (1 CPU)
Device:
tps Blk_read/s Blk_wrtn/s
Blk_read Blk_wrtn
sda
5.35
258.39 26.19
538210 54560
Device:
tps Blk_read/s Blk_wrtn/s
Blk_read Blk_wrtn
sda
0.00
0.00
0.00
0 0
Device:
tps Blk_read/s Blk_wrtn/s
Blk_read Blk_wrtn
sda
0.00
0.00
0.00
0 0
Device:
tps Blk_read/s Blk_wrtn/s
Blk_read Blk_wrtn
sda
0.00
0.00
0.00
0 0
/> iostat -d 1 #和上面的命令一样,也是每隔1秒刷新并输出一次,但是该命令将一直输出,直到按CTRL+C退出。
下面将给出输出表格中每列的含义:
列名 |
说明 |
Blk_read/s |
每秒块(扇区)读取的数量。 |
Blk_wrtn/s |
每秒块(扇区)写入的数量。 |
Blk_read |
总共块(扇区)读取的数量。 |
Blk_wrtn |
总共块(扇区)写入的数量。 |
iostat还有一个比较常用的选项-x,该选项将用于显示和io相关的扩展数据。
/> iostat -dx 1 3
Device: rrqm/s wrqm/s r/s w/s
rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
sda
5.27 1.31 2.82 1.14 189.49 19.50
52.75 0.53 133.04
10.74 4.26
Device: rrqm/s wrqm/s r/s w/s
rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
sda
0.00 0.00 0.00 0.00 0.00
0.00 0.00
0.00 0.00
0.00 0.00
Device: rrqm/s wrqm/s r/s w/s
rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
sda
0.00 0.00 0.00 0.00 0.00
0.00 0.00
0.00 0.00
0.00 0.00
还可以在命令行参数中指定要监控的设备名,如:
/> iostat -dx sda 1 3 #指定监控的设备名称为sda,该命令的输出结果和上面命令完全相同。
下面给出扩展选项输出的表格中每列的含义:
列名 |
说明 |
rrqm/s |
队列中每秒钟合并的读请求数量 |
wrqm/s |
队列中每秒钟合并的写请求数量 |
r/s |
每秒钟完成的读请求数量 |
w/s |
每秒钟完成的写请求数量 |
rsec/s |
每秒钟读取的扇区数量 |
wsec/s |
每秒钟写入的扇区数量 |
avgrq-sz |
平均请求扇区的大小 |
avgqu-sz |
平均请求队列的长度 |
await |
平均每次请求的等待时间 |
util |
设备的利用率 |
下面是关键列的解释:
util是设备的利用率。如果它接近100%,通常说明设备能力趋于饱和。
await是平均每次请求的等待时间。这个时间包括了队列时间和服务时间,也就是说,一般情况下,await大于svctm,它们的差值越小,则说明队列时间越短,反之差值越大,队列时间越长,说明系统出了问题。
avgqu-sz是平均请求队列的长度。毫无疑问,队列长度越短越好。
6. 当前运行进程的实时监控工具(pidstat):
pidstat主要用于监控全部或指定进程占用系统资源的情况,如CPU,内存、设备IO、任务切换、线程等。pidstat首次运行时显示自系统启动开始的各项统计信息,之后运行pidstat将显示自上次运行该命令以后的统计信息。用户可以通过指定统计的次数和时间来获得所需的统计信息。
在正常的使用,通常都是通过在命令行选项中指定待监控的pid,之后再通过其他具体的参数来监控与该pid相关系统资源信息。
选项 |
说明 |
-l |
显示该进程和CPU相关的信息(command列中可以显示命令的完整路径名和命令的参数)。 |
-d |
显示该进程和设备IO相关的信息。 |
-r |
显示该进程和内存相关的信息。 |
-w |
显示该进程和任务时间片切换相关的信息。 |
-t |
显示在该进程内正在运行的线程相关的信息。 |
-p |
后面紧跟着带监控的进程id或ALL(表示所有进程),如不指定该选项,将监控当前系统正在运行的所有进程。 |
#监控pid为1(init)的进程的CPU资源使用情况,其中每隔2秒刷新并输出一次,3次后程序退出。
/> pidstat -p 1 2 3 -l
07:18:58 AM
PID %usr %system %guest
%CPU CPU Command
07:18:59 AM
1 0.00 0.00 0.00
0.00 0 /sbin/init
07:19:00 AM
1 0.00 0.00
0.00 0.00 0 /sbin/init
07:19:01 AM
1 0.00 0.00
0.00 0.00 0 /sbin/init
Average:
1 0.00 0.00
0.00 0.00 - /sbin/init
%usr: 该进程在用户态的CPU使用率。
%system:该进程在内核态(系统级)的CPU使用率。
%CPU: 该进程的总CPU使用率,如果在SMP环境下,该值将除以CPU的数量,以表示每CPU的数据。
CPU: 该进程所依附的CPU编号(0表示第一个CPU)。
#监控pid为1(init)的进程的设备IO资源负载情况,其中每隔2秒刷新并输出一次,3次后程序退出。
/> pidstat -p 1 2 3 -d
07:24:49 AM
PID kB_rd/s kB_wr/s kB_ccwr/s Command
07:24:51 AM
1 0.00
0.00 0.00 init
07:24:53 AM
1 0.00
0.00 0.00 init
07:24:55 AM
1 0.00
0.00 0.00 init
Average:
1
0.00 0.00
0.00 init
kB_rd/s: 该进程每秒的字节读取数量(KB)。
kB_wr/s: 该进程每秒的字节写出数量(KB)。
kB_ccwr/s: 该进程每秒取消磁盘写入的数量(KB)。
#监控pid为1(init)的进程的内存使用情况,其中每隔2秒刷新并输出一次,3次后程序退出。
/> pidstat -p 1 2 3 -r
07:29:56 AM PID
minflt/s majflt/s VSZ
RSS %MEM Command
07:29:58 AM
1 0.00
0.00 2828 1368 0.13 init
07:30:00 AM
1 0.00
0.00 2828 1368 0.13 init
07:30:02 AM
1 0.00 0.00
2828 1368 0.13 init
Average:
1
0.00 0.00 2828
1368 0.13 init
%MEM: 该进程的内存使用百分比。
#监控pid为1(init)的进程任务切换情况,其中每隔2秒刷新并输出一次,3次后程序退出。
/> pidstat -p 1 2 3 -w
07:32:15 AM
PID cswch/s nvcswch/s Command
07:32:17 AM
1 0.00 0.00
init
07:32:19 AM
1 0.00 0.00
init
07:32:21 AM
1 0.00 0.00
init
Average:
1 0.00 0.00
init
cswch/s: 每秒任务主动(自愿的)切换上下文的次数。主动切换是指当某一任务处于阻塞等待时,将主动让出自己的CPU资源。
nvcswch/s: 每秒任务被动(不自愿的)切换上下文的次数。被动切换是指CPU分配给某一任务的时间片已经用完,因此将强迫该进程让出CPU的执行权。
#监控pid为1(init)的进程及其内部线程的内存(r选项)使用情况,其中每隔2秒刷新并输出一次,3次后程序退出。需要说明的是,如果-t选项后面不加任何其他选项,缺省监控的为CPU资源。结果中黄色高亮的部分表示进程和其内部线程是树状结构的显示方式。
/> pidstat -p 1 2 3 -tr
Linux 2.6.32-71.el6.i686 (Stephen-PC)
11/16/2011 _i686_ (1 CPU)
07:37:04 AM
TGID TID minflt/s
majflt/s VSZ RSS
%MEM Command
07:37:06 AM
1
- 0.00
0.00 2828
1368 0.13 init
07:37:06 AM
-
1 0.00
0.00 2828
1368 0.13 |__init
07:37:06 AM
TGID TID minflt/s
majflt/s VSZ RSS
%MEM Command
07:37:08 AM
1
- 0.00 0.00
2828 1368 0.13 init
07:37:08 AM
-
1 0.00
0.00 2828
1368 0.13 |__init
07:37:08 AM
TGID TID minflt/s
majflt/s VSZ RSS
%MEM Command
07:37:10 AM
1 -
0.00
0.00 2828
1368 0.13 init
07:37:10 AM
-
1 0.00
0.00 2828
1368 0.13 |__init
Average:
TGID TID minflt/s
majflt/s VSZ RSS
%MEM Command
Average:
1
- 0.00
0.00 2828
1368 0.13 init
Average:
-
1 0.00
0.00 2828
1368 0.13 |__init
TGID: 线程组ID。
TID: 线程ID。
以上监控不同资源的选项可以同时存在,这样就将在一次输出中输出多种资源的使用情况,如:pidstat -p 1 -dr。
7. 报告磁盘空间使用状况(df):
该命令最为常用的选项就是-h,该选项将智能的输出数据单位,以便使输出的结果更具可读性。
/> df -h
Filesystem
Size Used Avail Use% Mounted on
/dev/sda1
5.8G 3.3G 2.2G 61% /
tmpfs
504M 260K 504M 1% /dev/shm
8. 评估磁盘的使用状况(du):
选项 |
说明 |
-a |
包括了所有的文件,而不只是目录。 |
-b |
以字节为计算单位。 |
-k |
以千字节(KB)为计算单位。 |
-m |
以兆字节(MB)为计算单位。 |
-h |
使输出的信息更易于阅读。 |
-s |
只显示工作目录所占总空间。 |
--exclude=PATTERN |
排除掉符合样式的文件,Pattern就是普通的Shell样式,?表示任何一个字符,*表示任意多个字符。 |
--max-depth=N |
从当前目录算起,目录深度大于N的子目录将不被计算,该选项不能和s选项同时存在。 |
#仅显示子一级目录的信息。
/> du --max-depth=1 -h
246M ./stephen
246M .
/> du -sh ./* #获取当前目录下所有子目录所占用的磁盘空间大小。
352K ./MemcachedTest
132K ./Test
33M
./thirdparty
#在当前目录下,排除目录名模式为Te*的子目录(./Test),输出其他子目录占用的磁盘空间大小。
/> du --exclude=Te* -sh ./*
352K ./MemcachedTest
33M ./thirdparty
转载自Stephen Liu,仅做学习收藏用途。