有关从标准 Linux 文件中提取宝贵数据片段的几点建议
Linux 操作系统由许多文件组成。其中包括配置设置文件、文本文件、文档文件、日
志文件等等。通常,这些文件包含为查找重要数据而需要访问的信息。尽管您可以轻
松地使用标准实用程序(如 cat、more 等)将大多数文件的内容打印到屏幕,但某些
实用程序更适合于分析相关值。
本文介绍了 grep、cut、paste、join、sed 和 awk 等实用程序,但它并不是仅仅重
复参考手册页以及介绍每个实用程序存在的原因。相反,我们将首先了解每个实用程
序及其选项,将该实用程序应用于 Linux 文件中所通用的冒号分隔的文件格式,然后
了解每个工具为什么以及如何用于从文件中提取数据。
grep 的强大功能
名称 grep 是 Global Regular Expression Print(全局正则表达式打印)的缩写,
它实际上源自下面这个用于打印与指定模式匹配的所有行的命令:
g/re/p
其中,“re”表示“正则表达式”。自从 UNIX 及其各种同类产品(包括 Linux)推出以
来,grep 衍生了两个其他变体:fgrep(固定 grep)和 egrep(扩展
grep)。fgrep 实用程序只搜索固定字符串,而 egrep 因采用了一个更复杂的内部算
法而提供了更多功能。
在了解 grep、fgrep 和 egrep 的功能之前,必须先了解正则表达式的概念及其用
法。
使用正则表达式。就其最简单的形式而言,正则表达式是用于在文件中定位文本的搜
索条件。例如,要查找所有包含单词“add”的行,您可以搜索“add”—这样,“add”便构
成了一个正则表达式。如果不但要查找该单词,而且还要将其更改为“sum”,则可以在
实用程序中提供相应的命令将“add”替换为“sum”—这样,“add”和“sum”便构成了正则表
达式。
问题是“addition”将更改为“sumition”,“address”将更改为“sumress”。要解决此问
题,可以指定仅当某些项两侧各包含一个空格时才替换这些项—“ add ”变为“ sum ”。
该方法在大多数情况下均有效,仅当“add”是某一行的第一个或最后一个单词(意味着
它的两侧没有所需的空格来实现匹配)时,才会出现问题。
使用正则表达式可以增强灵活性。例如,如果只希望在“add”是一行中的前三个字符
的情况下进行更改,则可以使用插入符号 (^) 表示行首:“^add ”。相反,如果只希
望在指定的字母是一行中的后三个字母时进行更改,则可以使用美元符号 ($) 表示行
尾:“ add$”。
以下是正则表达式遵循的基本规则:
任何单个字符或字符序列可用于与自身进行匹配,如以上的“add”示例所示。
插入符号表示行首;美元符号表示行尾。
要按字面搜索一行中的特殊字符(如美元符号或插入符号),请在这些字符前加上一
个反斜杠 (\)。例如,“\$”搜索“$”而不是行尾。
句点 (.) 表示任何单个字符。例如,“uu3..2”表示六个字符项,其中的前三个字符
为“uu3”,最后一个字符为“2”。中间的两个字符可以是任意字符,但个数只能为两
个。
如果正则表达式包含在斜线中(如“/re/”),则将按从头到尾的顺序搜索文件。如果
它括在问号中(“?re?”),则按反向搜索文件。
方括号 ([]) 可用于表示多个值,而减号 (-) 则表示介于两个值之间的值。例
如,[0-9] 与 [0123456789] 相同,而 [a-z] 与 [abcdefghijklmnopqrstuvwxyz] 相
同。如果列表的第一个字符是插入符号,则它匹配列表之外的任何字符。下表总结了
这些匹配:
表达式匹配
示例 说明
[abc] 匹配 a、b、c 中的一个
[a-z] 匹配 a 到 z 中的任意一个小写字母
[A-Z] 匹配 A 到 Z 中的任意一个大写字母
[0-9] 匹配 0 到 9 中的任意一个数字
[^0-9] 从 0 到 9 的任意字符
[-0-9] 从 0 到 9 的任意字符或“-”
[0-9-] 从 0 到 9 或“-”中的任意字符
[^-0-9] 除 0 到 9 的和“-”外的任意字符
[a-zA-Z0-9] 匹配任意字母字符或数字字符
像 grep 一样看世界。有许多工具与 grep 实用程序相似,如大多数其他操作系统中
的 find 命令或一些编程语言中的 instr 命令。它用于在文件内查找与指定模式匹配
的项。
为了解释其用法和结果,我们需要一个用来处理的核心文件。下框代表一个名为
MEMO 的文件,将用于多个示例中:
TO: All Employees
FROM: Human Resources
Dist. Via: 73945 REARICK RALPH
73947 KELLEY PHIL
73949 DUGAN GEORGE
73950 WAGNER EVAN
73951 BOWL GORDON
73952 PALMER SCOTT
73954 MAY JAN
73955 HARRIS SHANNON
73956 BOYCE WILLIAM
73958 BRUMMER RICHARD
73960 WADDY RICHARD
73962 CHEESMAN BOB
In order to better serve the needs of our mass market customers, Alden
Publishing is integrating the groups selling to this channel for Alden
General Reference and Alden Computer Publishing.This change will allow us
to better coordinate our selling and marketing efforts, as well as
simplify Alden's relationships with these customers in the areas of
customer service, co-op management, and credit and collection.Two national
account managers, Susan April and Bob Cheesman, have joined the sales team
as result of these changes.
To achieve this goal, we have also organized the new mass sales group
into three distinct teams reporting to our current sales directors, Phil
Kelley and Ralph Rearick.I have outlined below the national account
managers and their respective accounts in each of the teams.We have also
hired two new national account managers and a new sales administrator to
complete our account coverage.They include
Jan May, who joins us from Acme Consumer Electronics as a national
account manager covering traditional mass merchants
Richard Waddy, who comes to us via PepsiCo and Proctor & Gamble and will
be responsible for managing our West Coast territory
Shannon Harris, who will become an account administrator for our
warehouse clubs business and joins us from Westvaco's Envelope division
Please join me in welcoming each of our new team members.
作为一个简单的示例,要查找包含单词“welcoming”的行,最好的方法是
$ grep welcoming MEMO
Please join me in welcoming each of our new team members.
$
如果要查找单词“market”,结果只略有不同:
$ grep market MEMO
In order to better serve the needs of the mass market customers,
Alden Publishing is integrating This change will allow us to
better coordinate our selling and marketing efforts, as well as
$
grep 实用程序在一个文件(或多个文件)的每一行中搜索给定字符串的第一个匹配
项。如果找到该字符串,则打印此行;否则不打印此行。以这种方式,在请求的
“market”和“marketing”之间找到匹配项。如果该文档中存在
“marketable”、“marketed”、“markets”以及其他类似单词,则还将找到这些单词的匹
配项。可以使用通配符和元字符,强烈建议将这些字符放在引号中,以便 shell 不会
将其当作命令。
要查找所有包含数字的行,请使用以下方法:
$ grep "[0-9]" MEMO
73945 REARICK RALPH
73947 KELLEY PHIL
73949 DUGAN GEORGE
73950 WAGNER EVAN
73951 BOWL GORDON
73952 PALMER SCOTT
73954 MAY JAN
73955 HARRIS SHANNON
73956 BOYCE WILLIAM
73958 BRUMMER RICHARD
73960 WADDY RICHARD
73962 CHEESMAN BOB
$
要查找所有包含“the”的行,请使用以下方法:
$ grep the MEMO
In order to better serve the needs of the mass market customers,
Alden Publishing is integrating
the groups selling to this channel for Alden General Reference and
Alden Computer Publishing.
simplify Alden's relationships with these customers in the areas
of customer service,
and Bob Cheesman have joined the sales team as result of these
changes.
To achieve this goal, we have also organized the new mass sales
group into three distinct teams
the national account managers and their respective accounts in
each of the teams.We have also
$
找到了六行,其中一行实际上包含单词“these”,余下几行包含单词“the”的精确匹
配。grep 实用程序(几乎与其他每个 UNIX/Linux 实用程序一样)区分大小写,这意
味着查找“The”(而不是“the”)将产生完全不同的结果:
$ grep The MEMO
coverage.They include:
$
如果不考虑文件中使用的大小写而要查找某个特定词组,则可以采用下面两种方法。
第一种方法是使用方括号查找“The”和“the”:
$ grep "[T,t]he" MEMO
In order to better serve the needs of the mass market customers,
Alden Publishing is integrating
the groups selling to this channel for Alden General Reference and
Alden Computer Publishing.
simplify Alden's relationships with these customers in the areas
of customer service,
and Bob Cheesman have joined the sales team as result of these
changes.
To achieve this goal, we have also organized the new mass sales
group into three distinct teams
the national account managers and their respective accounts in
each of the teams.We have also
coverage.They include:
$
第二种方法是使用“-i”选项,该选项指示 grep 不区分大小写:
$ grep -i the MEMO
In order to better serve the needs of the mass market customers,
Alden Publishing is integrating
the groups selling to this channel for Alden General Reference and
Alden Computer Publishing.
simplify Alden's relationships with these customers in the areas
of customer service,
and Bob Cheesman have joined the sales team as result of these
changes.
To achieve this goal, we have also organized the new mass sales
group into three distinct teams
the national account managers and their respective accounts in
each of the teams.We have also
coverage.They include:
$
除“-i”以外,您还可以使用几个其他选项来更改输出。最相关的选项包括:
“-c”—抑制标准输出;相反,打印每个输入文件的匹配行数。
“-l”—抑制标准输出;相反,打印通常从中打印输出的每个输入文件的名称。
“-n”—使用其输入文件中的行号为输出的每一行加上前缀。
“-v”—颠倒匹配的含义,选择不匹配的行。
使用 fgrep 和 egrep。fgrep 实用程序也可以称作快速 grep 和固定 grep。同
grep 一样,它在文件中搜索字符串,并打印所有包含该字符串的行。与 grep 不同的
是,fgrep 搜索字符串,而不是搜索与表达式匹配的模式。可以将 fgrep 看作是具有
几个增强功能的 grep:
您可以同时搜索多个对象。
fgrep 实用程序通常要比 grep 快很多。
您不能使用 fgrep 搜索带有模式的正则表达式。
例如,假设您要从前面的 MEMO 文件中找出大写名称。要查找“HARRIS”和“BOB”,必
须发出两个不同的 grep 命令:
$ grep HARRIS MEMO
73955 HARRIS SHANNON
$
$ grep BOB MEMO
73962 CHEESMAN BOB
$
您可以只使用一个 fgrep 命令来完成同一操作:
$ fgrep "HARRIS
> BOB" MEMO
73955 HARRIS SHANNON
73962 CHEESMAN BOB
$
注意,在各项之间需要有一个回车符。如果没有回车符,搜索将在每行上查找与
“HARRIS BOB”匹配的包含 10 个字母的字符串。如果有回车符,则查找“HARRIS”的匹
配项和“BOB”的匹配项。还应注意,必须在目标文本两侧使用引号。这样做是为了区分
文本和文件名。
您不必在命令行上指定搜索项,而是可以将其置于文件中并使用该文件的内容搜索其
他文件。使用 -f 选项,该技术使您可以创建一个包含您经常搜索的名称或数字的主
文件,并重复使用它:
$ cat pull_list
HARRIS
BOB
$
$ fgrep -f pull_list MEMO
73955 HARRIS SHANNON
73962 CHEESMAN BOB
$
同其前辈 grep 和 fgrep 一样,egrep用于在文件中搜索指定文本的匹配项。从本质
上而言,您可以将它看作是 grep 的一个更强大的版本,通过它一次可以搜索多个对
象。要搜索的对象由回车符分隔(与 fgrep 一样)或由管道符号 (|) 分隔:
$ egrep "HARRIS
> BOB" MEMO
73955 HARRIS SHANNON
73962 CHEESMAN BOB
$
$ egrep "HARRIS|BOB" MEMO
73955 HARRIS SHANNON
73962 CHEESMAN BOB
$
除了能够搜索多个对象以外,egrep 提供的 grep 所不具备的功能还包括能够搜索重
复项和组:
? 搜索前面字符的零个或一个重复项
+ 搜索前面字符的一个或多个重复项
( ) 表示一个组。
例如,假设您不知道 Jan 的姓是“May”还是“Mays”:
$ egrep "MAYS?"MEMO
73954 MAY JAN
$
该方法查找“MAY”和“MAYS”的匹配项,而
$ egrep "HARRIS+" MEMO
73955 HARRIS SHANNON
$
匹配“HARRIS”、“HARRISS”、“HARRISSS”,依此类推。如果要搜索某个单词及其可能
的派生词,则将派生词的特征字符括在括号中:
$ egrep -i "electron(ic)?s" MEMO
Jan May, who joins us from Acme Consumer Electronics as a National
Account Manager
$
该方法查找“electrons”和“electronics”的匹配项。因此,后跟“+”的正则表达式查
找此正则表达式的一个或多个匹配项,而后跟“?”的正则表达式查找此正则表达式的零
个或一个匹配项。由“|”或回车符分隔的多个正则表达式查找由其中任意表达式所查找
的字符串,且正则表达式可以用括号 ( ) 进行分组。可以使用的参数有
-c、-f、-i、-l、-n 和 -v。
实例。grep 实用程序系列可以用于任何文本格式的系统文件,在其行中查找匹配
项。例如,要在 /etc/passwd 文件中查找任何名为 Kristin 的用户项,可以使用以
下方法:
$ grep kristin /etc/passwd
kristin:petKv.fLWG/Ig:506:100:kristin dulaney:/home/kristin:/bin/bash
kscott:#Jknidies^v:610:100:kristin scott:/home/kscott:/bin/bash
$
注意:本文的很多示例都使用了 /etc/passwd,这是因为它代表了通用冒号分隔文
件。与其相关的规则可以适用于任何类似文件。
由于它在行中的任意位置搜索匹配项,因此它将找到 Kristin Dulaney 和 Kristin
Scott 两项。如果只希望搜索用户名(第一个域)为 kristin 的项,则可以按如下所
示修改此命令:
$ grep "^kristin" /etc/passwd kristin:petKv.fLWG/Ig:506:100:kristin
dulaney:/home/kristin:/bin/bash
$
使用 cut、paste 和 join
cut 实用程序能够将可能构成数据域的列与文件分离。默认分隔符为制表符,-f 选
项用于指定所需的域。例如,如果名为“august”的文本文件包含三列,如下所示:
one two three
four five six
seven eight nine
ten eleven twelve
则命令
$ cut -f2 august
将返回
two
five
eight
eleven
而
$ cut -f1,3 august
将返回相反的结果:
one three
four six
seven nine
ten twelve
该命令有多个可用选项。除 -f 以外的两个比较常见的选项是
-c—用于指定字符而非域
-d—用于指定除制表符以外的分隔符
其他 cut 选项。 ls -l 命令显示权限、链接数、所有者、组、大小、日期和文件名
—以上所有内容均由空格分隔,并在权限与链接之间包含两个字符。如果您只想知道谁
将文件保存在目录中,而不关注其他数据,则可以使用
$ ls -l | cut -d" " -f5
该命令忽略权限(第一个域)、两组空格(第二和第三个域)以及链接数(第四个
域)。随后,它将显示所有者(第五个域),并忽略其后的所有内容。获得此结果的
另一种方法是使用 ls -l,权限通常由 10 个字符组成,其中三个 3 字符空格,其后
是链接数和空格。所有者的名称通常从显示的第 16 个字符开始,并延续整个名称的
长度。命令
$ ls -l | cut -c16
返回第 16 个字符,即所有者名称的第一个字母。如果我们假设大多数用户将使用八
个或八个以下的字符表示其名称,则命令
$ ls -l | cut -c16-24
将返回名称域中的那些项。
文件名从第 55 个字符开始,但您无法确定该字符之后的字符数,这是因为某些文件
名要比其他文件名长很多。解决该问题的方法是从第 55 个字符开始,但不指定结束
字符(这意味着将占用行的全部剩余位置),如下所示:
$ ls -l | cut -c55-
cut 实用程序从文件中提取域,然后可以使用 paste 或 join 合并这些域。这两个
实用程序中,paste 相对比较简单;它只从源文件中提取一行,并将其与从其他源文
件中提取的另一行合并。例如,如果第一个文件的内容为
Indianapolis
Columbus
Peoria
Livingston
Scottsdale
第二个文件的内容为
Indiana
Ohio
Illinois
Montana
Arizona
则以下内容(包括提示)将为生成的结果:
$ paste fileone filetwo
Indianapolis Indiana
Columbus Ohio
Peoria Illinois
Livingston Montana
Scottsdale Arizona
$
如果第一个文件中的行数比第二个文件多,则只粘贴制表符之后的空白项。制表符是
默认分隔符,但可以使用 -d 选项将其更改为其他分隔符:
$ paste -d", " fileone filetwo
Indianapolis, Indiana
Columbus, Ohio
Peoria, Illinois
Livingston, Montana
Scotttsdale, Arizona
$
也可以使用 -s 选项将第一个文件的所有数据输出到一行,其后依次是一个回车符和
第二个文件:
$ paste -s fileone filetwo
Indianapolis Columbus Peoria Livingston Scotttsdale
Indiana Ohio Illinois Montana Arizona
$
可以将 join 实用程序看作是 paste 的显著增强版本。但该实用程序只有在所联接
的文件存在一个公共域时才起作用,这一点非常重要。例如,如果像前面 paste 示例
中那样使用 join,则结果将如下所示:
$ join fileone filetwo
$
换言之,不显示任何内容。join 实用程序必须在所针对的两个文件中找到一个公共
域,并且在默认情况下,它要求此公共域是第一个域。例如,假设第一个文件现在包
含以下项:
11111 Indianapolis
22222 Columbus
33333 Peoria
44444 Livingston
55555 Scottsdale
第二个文件的内容包括
11111 Indiana 500 race
22222 Ohio Buckeye State
33333 Illinois Wrigley Field
44444 Montana Yellowstone Park
55555 Arizona Grand Canyon
以下内容(包括提示)是生成的结果:
$ join fileone filetwo
11111 Indianapolis Indiana 500 race
22222 Columbus Ohio Buckeye State
33333 Peoria Illinois Wrigley Field
44444 Livingston Montana Yellowstone Park
55555 Scottsdale Arizona Grand Canyon
$
已识别第一个域相同,并合并了匹配项。paste 盲目地从每个文件中提取一行以创建
显示,而 join 只合并匹配的行(这一点至关重要),且该匹配必须是与另一个文件
中相应行的完全匹配。对这一点怎么强调都不过分。例如,如果第二个文件在中间额
外增加了一行:
11111 Indiana 500 race
22222 Ohio Buckeye State
66666 Tennessee Smokey Mountains
33333 Illinois Wrigley Field
44444 Montana Yellowstone Park
55555 Arizona Grand Canyon
则以下内容(包括提示)是生成的结果:
$ join fileone filetwo
11111 Indianapolis Indiana 500 race
22222 Columbus Ohio Buckeye State
$
只要文件不再匹配,就不再执行操作。检查而且仅检查两个文件中的对应行,在默认
域上查找匹配。如果找到匹配,则将其合并到结果中;否则不合并。使用原始的第二
个文件再进行一次演示:
$ tac filetwo > filethree
$ join fileone filethree
55555 Scottsdale Arizona Grand Canyon
$
尽管这两个文件中的每一行都匹配,但只找到一个匹配项。强烈建议您首先对每个要
使用的文件进行排序以使其采用相同的顺序来解决 join 存在的问题。
您不必保留 join 的默认设置,即只查看前几个域是否匹配,或输出所有列。-1 选
项使您能够指定将哪个域用作第一个文件中的匹配域,-2 选项使您能够指定将哪个域
用作第二个文件中的匹配域。例如,如果第一个文件的第二个域匹配第二个文件的第
三个域,则语法为
$ join -1 2 -2 3 fileone filethree
-o 选项用于以格式 {file.field} 指定输出域。因此,要在匹配行上打印第一个文
件的第二个域和第二个文件的第三个域,则语法为
$ join -o 1.2 2.3 fileone filethree
Indianapolis 500
Columbus Buckeye
Peoria Wrigley
Livingston Yellowstone
Scottsdale Grand
$
实例。由于 cut 可用于从文件中提取域,因此它对于处理系统文件是不可或缺的。
例如,要获取系统上所有用户的列表,可以从先前示例中使用的 /etc/passwd 文件中
只提取第一个域:
$ cut -d":"-f1 /etc/passwd
root
daemon
bin
sys
adm
uucp
mail
kristin
$
要收集用户名及其相应的主目录,可以提取第一个和第六个域:
$ cut -d":"-f1 /etc/passwd
root:/
daemon:/
bin:/usr/bin
sys:/
adm:/var/adm
uucp:/usr/lib/uucp
mail:/etc/mail
kristin:/home/kristin
$
join 实用程序最明显的示例是从 /etc/passwd 文件中提取用户名和相应的主目录,
并从 /etc/group 文件中提取 alpha 组名称。组以数字格式显示在 /etc/passwd 文
件的第四个域(该格式就是组在 /etc/group 文件的第三个域中的显示格式)中:
$ join -1 4 -2 3 -o 1.1 2.1 1.6 -t :/etc/passwd /etc/group
千万别忽视 sed 和 awk
资源
下载用于 Linux 的 Oracle 数据库 10g
Oracle 数据库 10g 第 1 版 (10.1.0.2) 现在可用于 Linux x86 和 Linux
Itanium 平台;请在此从 OTN 上免费下载。
访问 Linux 技术中心
收藏本页,以获取有关 Linux 系统管理员最佳实践的一般技术信息,以及有关
Oracle-on-Linux 产品群的具体技术信息。
相关文章
Linux 相关技术文章存档
Linux 工具中两个最强大的实用程序是 sed 和 awk。sed 流编辑器可用于编辑大量
数据,而不需要任何的手动干预。awk 实用程序本身实际上是一种编程语言,可用于
复杂的逻辑语句以及简化文本片段的提取。
这两个实用程序是先前文章的主题;建议您在进行下一个示例前先查看这些文章。
实例。 要从 /etc/passwd 文件中提取第六个域,请使用命令
$ awk -F:'{print $6}' /etc/passwd
要打印该文件(使用短划线代替域之间的分隔符)并只打印第一个和第六个域(按相
反的顺序),请使用命令
$ awk -F":"'{OFS="-"}{print $6,$1}}' /etc/passwd
结论
本文重点介绍了用于从标准 Linux 文件中提取数据的实用程序。提取数据后,可以
整理数据以查看和打印,或将数据导入其他数据库。知道如何使用这几个工具可以帮
助您减少普通任务所需的时间,使您成为一个更高效的管理员。