精通Linux系列十:如何在4个G的日志中找到错误记录(文件文本操作)
文件文本操作
命令 | 含义 |
grep | 在文件中查找符合正则表达式的行。 |
cut | 从文件中提取列。 |
paste | 追加列。 |
tr | 将字符翻译成其他字符。 |
expand,``unexpand | 在制表符和空格之间转换。 |
sort | 按各种标准对文本行进行排序。 |
uniq | 在文件中定位相同的行。 |
tee | 复制文件并在标准输出上同时打印它。 |
Linux最大的优点就是文本操作:我们通过应用变换,将文本文件(或标准输入)调整到所需的形式,这个操作常常是在管道中进行的。任何读取标准输入和写入标准输出的程序都属于这个范畴,这里我们将介绍一些最重要的工具。
grep
stdin stdout - file -- opt --help --version
grep [选项] 模式 [文件]
grep
(grep) 命令是Linux工具库中最常用、最强大的工具之一。它的基本理念很简单:给定一个或多个文件,打印出所有符合特定正则表达式模式的行。例如,如果一个文件 randomlines(随机行) 包含以下行:
棕色狐狸快速的跳过去了!
我妈妈刚给我们煎了九个煎饼。
电影在十一点。
如果我们搜索包含“煎饼”的所有行,我们得到:
→ grep pancake randomlines
我妈妈刚给我们煎了九个煎饼。
现在我们用一个正则表达式来匹配以感叹号结尾的行:
→ grep '\!$' randomlines
棕色狐狸快速的跳过去了!
grep
(grep) 可以使用两种不同类型的正则表达式,称为 basic(基本) 和 extended(扩展)。基本语法在 Table 2 中。学习正则表达式是非常值得的。很多Linux程序也使用它们,比如 sed
(sed) 和 perl
(perl)。
有用的选项
-v | 只打印不符合正则表达式的行。 |
-l | 只打印包含匹配行的文件的名称,而不是行本身。 |
-L | 只打印不包含匹配行的文件的名称。 |
-c | 只打印匹配行的计数。 |
-n | 在每一行匹配的输出前面,打印其原始行号。 |
-b | 在每一行匹配的输出前面,打印该行在输入文件中的字节偏移。 |
-i | 大小写不敏感匹配。 |
-w | 只匹配完整的单词(即,与整个正则表达式匹配的单词)。 |
-x | 只匹配完整的行(即,与整个正则表达式匹配的行)。覆盖 -w 。 |
-A N | 在每个匹配行后,打印其文件中的下一个 N 行。 |
-B N | 在每个匹配行前,打印其文件中的前一个 N 行。 |
-C N | 与 -A N -B N 相同:打印每个匹配行上方 和 下方的 N 行(来自原始文件)。 |
--color=always | 用颜色突出显示匹配的文本,以提高可读性。 |
-r | 递归搜索目录及其子目录中的所有文件。 |
-E | 使用扩展正则表达式。参见 egrep (egrep)。 |
-F | 使用固定字符串列表而非正则表达式。参见 fgrep (fgrep)。 |
egrep
stdin stdout - file -- opt --help --version
egrep [选项] 模式 [文件]
egrep
(egrep) 命令就像 grep
(grep),但是使用不同的(“扩展的”)语言进行正则表达式。它与 grep -E
是相同的。
表达式 | 含义 |
. | 任意单一字符。 |
[...] | 匹配此列表中的任意单一字符。 |
[^...] | 匹配此列表中不存在的任意单一字符。 |
(...) | 分组。 |
| | ` |
^ | 行首。 |
$ | 行尾。 |
\< | 单词的开始。 |
\> | 单词的结束。 |
[:alnum:] | 任何字母数字字符。 |
[:alpha:] | 任何字母字符。 |
[:cntrl:] | 任何控制字符。 |
[:digit:] | 任何数字。 |
[:graph:] | 任何图形字符。 |
[:lower:] | 任何小写字母。 |
[:print:] | 任何可打印字符。 |
[:punct:] | 任何标点符号。 |
[:space:] | 任何空格字符。 |
[:upper:] | 任何大写字母。 |
[:xdigit:] | 任何十六进制数字。 |
* | 一个或多个重复的正则表达式。 |
\+ | + 正则表达式重复一次或多次。 |
\? | ? 正则表达式出现零次或一次。 |
\{ n \} | { n } 正则表达式重复*n *次。 |
\{ n ,\} | { n ,} 正则表达式重复*n *次或更多次。 |
\{ n , m \} | { n , m } 正则表达式重复次数在*n 到m (含)之间,n * < *m *。 |
\ c | 字符*c 的字面值,即使c 是特殊的正则表达式字符。例如,使用 * 来匹配星号或者 \ 来匹配反斜杠。或者,将字面字符放在方括号中,比如 [] 或 []。 |
GREP 和行结束字符
当你使用 grep
(grep) 匹配行尾($
)时,如果文本文件是在Microsoft Windows或Mac OS X系统上创建的,可能会出现奇怪的结果。每个操作系统对行结束的标准都不同。在Linux上,文本文件中的每一行都以一个换行符结束(ASCII 10)。在Windows上,文本行以回车(ASCII 13)结束,后面跟着一个换行符。而在OS X上,一个文本文件可能只用换行符或回车符结束每一行。如果 grep
不能正确匹配行尾,可以使用 cat -v
检查非Linux的行尾字符,它会将回车符显示为 ^M
:
→ cat -v dosfile.
哎呀!这个文件似乎在每行的结尾处使用了^M
在换行符之前有回车符。^M
要删除回车符,可以使用 tr -d
命令:
→ tr -d '\r' < dosfile. > linuxfile.
→ cat -v linuxfile.
哎呀!这个文件似乎在每行的结尾处使用了
在换行符之前有回车符。
fgrep
stdin stdout - file -- opt --help --version
fgrep [选项] [固定字符串] [文件]
fgrep
(fgrep) 命令就像 grep
(grep),但是它接受的是由换行符分隔的固定字符串列表,而不是正则表达式。它与 grep -F
是相同的。例如,如果你有一个每行都是字符串的字典文件:
→ cat my_dictionary_file
aardvark
aback
abandon
...
你可以方便地在一组输入文件中搜索这些字符串:
→ fgrep -f my_dictionary_file story
a little aardvark who went to
visit the abbot at the abbey.
通常,你会使用小写的 -f
选项让 fgrep
从文件中读取固定字符串。你也可以使用引号在命令行中读取固定字符串,但这有点复杂。要在文件中搜索字符串“one”、“two”和“three”,你需要输入:
→ fgrep 'one 注意我们在输入换行符
two
three' myfile
当搜索*和{等非字母数字字符时,fgrep
很方便,因为它们会被当作字面值,而不是正则表达式字符。
cut
stdin stdout - file -- opt --help --version
cut -(b|c|f)range [选项] [文件]
cut
(cut) 命令从文件中提取文本的列。“列”是由字符偏移定义的(例如,每行的第19个字符):
→ cut -c19 myfile
或者由字节偏移定义(如果你的语言有多字节字符,那么字节和字符是不同的):
→ cut -b19 myfile
或者由分隔字段定义(例如,在一个逗号分隔的文件data.csv中的每一行的第五个字段):
→ cat data.csv
one,two,three,four,five,six,seven
ONE,TWO,THREE,FOUR,FIVE,SIX,SEVEN
1,2,3,4,5,6,7
→ cut -f5 -d, data.csv
five
FIVE
5
你不仅限于输出一个单列:你可以给出范围(3-16
),逗号分隔序列(3,4,5,6,8,16
)或两者都有(3,4,8-16
)。对于范围,如果你省略了第一个数字(-16
),则默认为1(1-16
);如果你省略了最后一个数字(5-
),则默认到行尾。
有用的选项
-d C | 使用字符 C 作为 -f 选项之间的 输入 分隔符,默认为制表符。 |
--output-delimiter= C | 使用字符 C 作为 -f 选项之间的 输出 分隔符,默认为制表符。 |
-s | 抑制(不打印)不包含分隔符的行。 |
paste
stdin stdout - 文件 -- 选项 --help --version
paste [选项] [文件]
paste
命令与cut
命令恰好相反:它将多个文件视为垂直列并在标准输出中合并它们:
→ cat letters
A
B
C
→ cat numbers
1
2
3
4
5
→ paste numbers letters
1 A
2 B
3 C
4
5
→ paste letters numbers
A 1
B 2
C 3
4
5
有用的选项
-d delimiters | 在列之间使用给定的 delimiters 字符;默认为制表符。提供一个字符(-d: )总是使用,或者一个字符列表(-dxyz )在每一行上按顺序应用(第一个分隔符是x,然后是y,然后是z,然后是x,然后是y,...)。 |
-s | 横向:转置输出的行和列:→ ** paste -s letters numbers** A B C 1 2 3 4 5 |
tr
stdin stdout - 文件 -- 选项 --help --version
tr [选项] charset1 [charset2]
tr
命令执行一些简单的、有用的字符集转换。例如,要把文件中的所有内容大写:
→ cat wonderfulfile
This is a very wonderful file.
→ cat wonderfulfile | tr 'a-z' 'A-Z'
THIS IS A VERY WONDERFUL FILE.
或者把所有元音改为星号:
→ cat wonderfulfile | tr aeiouAEIOU '*'
Th*s *s * v*ry w*nd*rf*l f*l*.
或者删除所有元音:
→ cat wonderfulfile | tr -d aeiouAEIOU
Ths s vry wndrfl fl.
作为一个实际的例子,从DOS文本文件中删除所有回车符,使其与grep
等Linux文本工具更兼容:
→ tr -d '\r' < dosfile. > linuxfile.
tr
将 charset1
中的第一个字符转换为 charset2
中的第一个字符,第二个转换为第二个,第三个转换为第三个,依此类推。如果 charset1
的长度为 *N
*,则只使用 charset2
中的前 N
个字符。(如果 charset1
比 charset2
长,请参阅 -t
选项。)
字符集可以有以下形式:
形式 | 含义 |
ABDG | 字符A、B、D、G的序列。 |
A-Z | 从A到Z的字符范围。 |
[x*y] | 字符 x 的 y 重复。 |
[: class :] | 同grep 接受的字符类([:alnum:] 、[:digit:] 等)。 |
tr
还理解printf
接受的转义字符“\a”(^G
= 响铃警告),“\b”(^H
= 退格),“\f”(^L
= 换页),“\n”(^J
= 新行),“\r”(^M
= 返回),“\t”(^I
= 制表符),以及“\v”(^K
= 垂直制表符),以及符号*nnn
*表示八进制值为 nnn
的字符。
tr
适用于快速简单的翻译,但对于更强大的任务,请考虑使用sed
、awk
或perl
。
有用的选项
-d | 从输入中删除 charset1 中的字符。 |
-s | 从输入中删除 charset1 中找到的相邻重复项。例如,tr -s aeiouAEIOU 会将相邻的重复元音压缩为单个元音(reeeeeeally 会变成 really)。 |
-c | 补码:对 charset1 中未找到的所有字符进行操作。 |
-t | 如果 charset1 比 charset2 长,通过截短 charset1 使它们长度相同。如果没有 -t ,则 charset2 的最后一个字符(看不见的)会重复,直到 charset2 与 charset1 的长度相同。 |
expand
stdin stdout - file -- opt --help --version
expand [options] [files]
unexpand [options] [files]
expand(展开)
命令将制表符转换为看起来等效的空格字符数量,unexpand(取消展开)
则执行相反的操作。默认情况下,每八个空格处就有一个制表位,但你可以通过选项更改这个设置。这两个程序默认都写入标准输出。
→ expand tabfile > spacefile
→ expand spacefile > tabfile
要检查文件中是否包含空格或制表符,使用 cat -T
命令,它将制表符显示为 ^I
,或者 od -c
命令,它将制表符显示为 \t
。
Useful options
-t N | 指定每 N 个空格就有一个制表位。 |
sort
stdin stdout - file -- opt --help --version
sort [options] [files]
sort(排序)
命令按照字母顺序或者你指定的其他规则打印文本行。所有提供的文件都被连接起来,然后对结果进行排序并打印:
→ cat threeletters
def
xyz
abc
→ sort threeletters
abc
def
xyz
Useful options
-f | 忽略大小写排序。 |
-n | 数字排序(例如,9在10之前),而不是字母排序(10在9之前,因为它以“1”开头)。 |
-g | 另一种数字排序方法,使用不同的算法,该算法在其他方面认识到科学记数法(7.4e3表示“7.4乘以10的三次方”,即7400)。运行 info sort 以获取完整的技术细节。 |
-u | 唯一排序:忽略重复行。(如果与 -c 一起用于检查已排序的文件,如果有任何连续的行是相同的,就会失败。) |
-c | 不排序,只检查输入是否已经排序。如果是,什么也不打印;否则,打印一个错误消息。 |
-b | 忽略行的开头空格。 |
-r | 反转输出:从大到小排序。 |
-t X | 将 X 用作 -k 选项的字段分隔符。 |
-k key | 选择排序键。(与 -t 结合使用,选择键之间的分隔符字符。) |
排序键表示在排序时要考虑的行的一部分,而不是整行。一个例子可能是每行的第五个字符。通常,sort
会认为这些行是排序的:
aaaaz
bbbby
但是如果你的排序键是“每行的第五个字符”,表示为 -k1.5
,那么行会被反转,因为 y
在 z
之前。更实用的例子包括这个包含姓名和地址的文件:
→ cat people
George Washington,123 Main Street,New York
Abraham Lincoln,54 First Avenue,San Francisco
John Adams,39 Tremont Street,Boston
一个普通的排序会首先显示“Abraham Lincoln”这行。但是如果你把每行看作三个逗号分隔的值,你可以用以下命令按第二个值进行排序:
→ sort -k2 -t, people
George Washington,123 Main Street,New York
John Adams,39 Tremont Street,Boston
Abraham Lincoln,54 First Avenue,San Francisco
其中,“123 Main Street”在字母上是第一位。同样,你可以按城市(第三个值)进行排序:
→ sort -k3 -t, people
John Adams,39 Tremont Street,Boston
George Washington,123 Main Street,New York
Abraham Lincoln,54 First Avenue,San Francisco
项目 | 意义 | 默认值 |
F1 | 起始字段 | 必须提供 |
C1 | 起始字段1内的起始位置 | 1 |
F2 | 结束字段 | 最后一个字段 |
C2 | 结束字段内的起始位置 | 1 |
因此,sort -k1.5
基于第一个字段进行排序,从第五个字符开始;而sort -k2.8,5
意味着“从第二个字段的第八个字符开始,到第五个字段的第一个字符结束。”-t
选项改变了-k
的行为,所以它会考虑到像逗号这样的分隔符字符,而不是空格。
你可以重复-k
选项来定义多个键,这些键将按照命令行中找到的顺序从第一个到最后一个应用。
uniq
stdin stdout - file -- opt --help --version
uniq [options] [files]
uniq
(唯一)命令对文本的连续重复行进行操作。例如,如果你有一个文件 myfile:
→ cat letters2
a
b
b
c
b
那么uniq
会检测并处理两个连续的b,但不会处理第三个b:
→ uniq letters2
a
b
c
b
uniq
经常在对文件进行排序后使用:
→ sort letters2 | uniq
a
b
c
在这种情况下,只剩下一个b,因为所有三个b都通过sort
变成了相邻的,然后通过uniq
压缩成一个。另外,你也可以统计重复行的数量,而不是消除它们:
→ sort letters2 | uniq -c
1 a
3 b
1 c
有用的选项
-c | 计数相邻重复行。 |
-i | 大小写不敏感的操作。 |
-u | 只打印唯一的行。 |
-d | 只打印重复的行。 |
-s N | 在检测重复时,跳过每行的前*N *个字符。 |
-f N | 在检测重复时,忽略每行前*N *个以空格分隔的字段。 |
-w N | 在检测重复时,只考虑每行的前*N 个字符。如果与-s 或-f 一起使用,sort 将首先忽略指定数量的字符或字段,然后考虑接下来的N *个字符。 |
tee
stdin stdout - file -- opt --help --version
tee [options] files
像cat
命令一样,tee
(茶)命令将标准输入复制到标准输出,不进行任何更改。同时,它也将相同的标准输入复制到一个或多个文件。tee
通常出现在管道的中间,将一些中间数据写入文件,同时也将其传递给管道中的下一个命令:
→ who | tee original_who | sort
barrett pts/1 Sep 22 21:15
byrnes pts/0 Sep 15 13:51
silver :0 Sep 23 20:44
silver pts/2 Sep 22 21:18
这个命令行在屏幕上产生who
的排序输出,但同时也将who
的原始,未排序的输出写入到文件 original_who:
→ cat original_who
silver :0 Sep 23 20:44
byrnes pts/0 Sep 15 13:51
barrett pts/1 Sep 22 21:15
silver pts/2 Sep 22 21:18
然后将同样的输出传递到管道的其余部分(sort
),在屏幕上产生排序的输出。
有用的选项
-a | 附加,而不是覆盖文件。 |
-i | 忽略中断信号。 |
更强大的操作
我们仅仅接触了Linux文本过滤的冰山一角。Linux拥有数百种过滤器,能够实现对数据的更复杂的处理。但是,强大的功能往往意味着陡峭的学习曲线,这对于一篇文章来说太多了。以下是一些可以帮助你入门的过滤器。
awk
AWK是一种基于模式匹配的语言。它通过正则表达式匹配数据,然后根据数据执行操作。以下是一些处理文本文件myfile的简单示例。
打印每行的第二个和第四个单词:
→ awk '{print $2, $4}' myfile
打印所有字符数少于60个的行:
→ awk 'length < 60 {print}' myfile
sed
就像AWK一样,sed也是一个模式匹配引擎,可以对文本行进行操作。它的语法与vim和行编辑器ed密切相关。以下是一些简单的示例。
打印文件,将所有出现的字符串“me”更改为“YOU”:
→ sed 's/me/YOU/g' myfile
打印文件,去掉前10行:
→ sed '1,10d' myfile
m4
m4是一种宏处理语言和命令。它在文件中查找关键字,并为它们替换值。例如,给定此文件:
→ cat substitutions
My name is NAME and I am AGE years old.
ifelse(QUOTE,yes,Learn Linux today!)
看看m4如何对NAME
,AGE
和QUOTE
进行替换:
→ m4 -DNAME=Sandy substitutions
My name is Sandy and I am AGE years old.
→ m4 -DNAME=Sandy -DAGE=25 substitutions
My name is Sandy and I am 25 years old.
→ m4 -DNAME=Sandy -DAGE=25 -DQUOTE=yes substitutions
My name is Sandy and I am 25 years old.
Learn Linux today!
发表评论