目录
一.grep
(1)什么是grep
(2)grep的常用选项
(3)高级用法
1)grep配合正则表达式
2)grep与管道搭配
二.sed
1.sed编辑器介绍
(1)基本语法格式
1) 一些基本的编辑功能:
2. 更多的替换选项:
(1) 替换标记
(2) 替换字符
3.使用地址
(1)数字方式的行寻址
(2)使用文本模式过滤器
(3)命令组合
4.删除行
5.插入和附加文本
6.修改行
7.转换命令
8.打印行
(1)打印行
(2)打印行号
(3)列出行
9.使用sed处理文件
(1)写入文件
(2)从文件读取数据
三.awk
1.什么是awk
2.awk的语法格式及常用选项
3.awk的基本使用
(1)从命令行读取程序脚本
(2)使用数据字段变量
(3)在脚本中使用多个命令
(4)从文件中读取程序
(5)在处理数据前运行脚本
(6)在处理数据后运行脚本
4.awk的高级用法
(1)使用变量
1)使用内建变量
1. 数据变量
2)自定义变量
1.在脚本中给变量赋值
2.在命令行上给变量赋值
(2)处理数组
1)定义数组变量
2)遍历数组变量
3)删除数组变量
(3)使用模式
1)正则表达式
2)匹配操作符
3)数学表达式
(4)结构化命令
1)if 语句
2)while 语句
3)do-while 语句
4)for 语句
(5)内建函数
1)数学函数
2)字符串函数
3)时间函数
(6)自定义函数
1)定义函数
2)使用自定义函数
3)创建函数库
grep是一个强大的文本搜索工具,它可以通过指定的模式在文件或输出流中搜索匹配的行。
它的基本语法如下:
grep [options] pattern [file...]
其中,`options`是可选的选项,用来修改`grep`的行为。`pattern`是要搜索的字符串或正则表达式。`files`是要搜索的文件名,可以同时搜索多个文件。如果省略`files`参数,则默认从标准输入中读取数据。
例如,要在文件`file.txt`中搜索字符串`hello`,并打印匹配的行,可以使用以下命令:
grep hello file.txt
匹配的行将被显示。如果有多个匹配行,它们都将被显示。您可以使用选项来修改`grep`的行为。例如,使用`-i`选项可以忽略大小写进行匹配。
`grep`有许多选项可以用来修改其行为。以下是一些常用的grep选项:
常用选项 | 释义 |
---|---|
-i | 忽略大小写进行匹配。 |
-v | 反向查找,只打印不匹配的行。 |
-n | 显示匹配行的行号。 |
-r | 递归查找子目录中的文件。 |
-l | 只打印匹配的文件名。 |
-c | 只打印匹配的行数。 |
-w | 只匹配整个单词,而不是部分匹配。 |
-o | 只显示匹配的部分,而不是整行。 |
-A n | 显示匹配行及后面n行的内容。 |
-B n | 显示匹配行及前面n行的内容。 |
-C n | 显示匹配行及前后各n行的内容。 |
-e pattern | 指定多个匹配模式。 |
-E | 使用扩展正则表达式进行匹配。 |
-F | 将模式视为固定字符串而不是正则表达式。 |
以下是一些grep的常用选项的示例:
1. 搜索指定文件中的文本:
grep pattern file.txt
2. 在多个文件中搜索文本:
grep pattern file1.txt file2.txt
3. 递归地搜索目录中的文本:
grep -r pattern directory/
4. 忽略大小写进行搜索:
grep -i pattern file.txt
5. 显示匹配的行号:
grep -n pattern file.txt
6. 只显示匹配的文本:
grep -o pattern file.txt
7. 输出不匹配的行:
grep -v pattern file.txt
8. 使用正则表达式进行搜索:
grep -E "pattern" file.txt
9. 仅搜索文件名而不搜索文件内容:
grep -l pattern file*
`grep`可以与正则表达式一起使用,以执行更复杂的搜索。正则表达式是一种描述文本模式的语言,可以用来匹配复杂的文本模式。
例如,要在文件`file.txt`中查找以数字开头的行,可以使用以下命令:
grep "^[0-9]" file.txt
在这个例子中,`^[0-9]`是一个正则表达式,它匹配以数字开头的行。`^`表示行的开头,`[0-9]`表示一个数字字符。
您可以使用许多不同的正则表达式元字符来构建复杂的模式。例如:
元字符 | 说明 |
---|---|
. | 匹配任意单个字符。 |
* | 匹配前面的字符零次或多次。 |
+ | 匹配前面的字符一次或多次。 |
? | 匹配前面的字符零次或一次。 |
{n} | 匹配前面的字符恰好`n`次。 |
{n,} | 匹配前面的字符至少`n`次。 |
{n,m} | 匹配前面的字符至少`n`次,但不超过`m`次。 |
^ | 匹配以特定字符开头的行 |
$ | 匹配以特定字符结尾的行 |
[0-9] | 使用字符类匹配特定范围的字符 |
下面是一些示例:
1. 使用基本正则表达式匹配:
grep 'pattern' file.txt
2. 使用扩展正则表达式匹配:
grep -E 'pattern' file.txt
3. 匹配以特定字符开头的行:
grep '^start' file.txt
4. 匹配以特定字符结尾的行:
grep 'end$' file.txt
5. 匹配包含特定单词的行:
grep '\bword\b' file.txt
6. 匹配多个可能的模式:
grep 'pattern1\|pattern2' file.txt
7. 使用反向引用匹配重复的模式:
grep '\(abc\)\{2\}' file.txt
8. 使用字符类匹配特定范围的字符:
grep '[0-9]' file.txt
9. 使用量词匹配出现次数:
grep 'a\{2,4\}' file.txt
例如,要在进程列表中搜索包含`chrome`的进程,可以使用以下命令:
ps aux | grep chrome
在这个例子中,`ps aux`命令用于显示当前运行的进程列表。它的输出被传递给`grep`命令,后者搜索包含字符串`chrome`的行。
还可以将多个命令连接在一起,以创建更复杂的管道。
例如,要在进程列表中搜索包含`chrome`的进程,并显示进程ID,可以使用以下命令:
ps aux | grep chrome | awk '{print $2}'
在这个例子中,我们使用了三个命令:`ps aux`用于显示进程列表,`grep chrome`用于搜索包含字符串`chrome`的行,最后,`awk '{print $2}'`用于打印每行的第二个字段(即进程ID)。
以下是一些使用grep和管道的示例:
1. 将grep与其他命令一起使用,过滤结果:
command | grep 'pattern'
2. 使用grep过滤文件中的内容,并将结果输出到另一个文件:
grep 'pattern' file.txt | grep 'another_pattern' > output.txt
3. 使用grep与其他命令组合,进行文本处理:
cat file.txt | grep 'pattern' | sed 's/old/new/g' | awk '{print $1}'
4. 将grep与排序命令结合,按特定条件排序匹配的结果:
command | grep 'pattern' | sort -n
5. 使用grep过滤日志文件,并仅显示特定时间范围内的内容:
cat logfile.txt | grep 'pattern' | awk '/10:00:00/,/12:00:00/'
sed编辑器被称作流编辑器( stream editor),和普通的交互式文本编辑器恰好相反。在交互式文本编辑器中(比如vim),你可以用键盘命令来交互式地插入、删除或替换数据中的文本。流编辑器则会在编辑器处理数据之前基于预先提供的一组规则来编辑数据流。
它是文本处理中非常有用的工具,能够完美的配合正则表达式使用,处理时,把当前处理的行存储在临时缓冲区中,称为模式空间,接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变。
sed编辑器可以根据命令来处理数据流中的数据,这些命令要么从命令行中输入,要么存储在 一个命令文本文件中。
sed编辑器会执行下列操作。
(1) 一次从输入中读取一行数据。
(2) 根据所提供的编辑器命令匹配数据。
(3) 按照命令修改流中的数据。
(4) 将新的数据输出到STDOUT。
在流编辑器将所有命令与一行数据匹配完毕后,它会读取下一行数据并重复这个过程。在流编 辑器处理完流中的所有数据行后,它就会终止。由于命令是按顺序逐行给出的, sed编辑器只 需对数据流进行一遍处理就可以完成编辑操作。这使得sed编辑器要比交互式编辑器快得多, 你可以快速完成对数据的自动修改。
sed的基本语法格式如下:
sed options script file
script参数指定了应用于流数据上的单个命令。如果需要用多个命令,要么使用-e选项在命令行中指定,要么使用-f选项在单独的文件中指定。
常用的选项包括:
- `-n`:禁止自动输出模式空间的内容。
- `-i`:直接修改输入文件,而不是输出到标准输出。
- `-r`:启用扩展正则表达式。
1). 一些基本的编辑功能:
替换:使用`s`命令进行替换。
例如,将文本中的"old"替换为"new":`sed 's/old/new/' file.txt`
sed 's/old/new/g' file.txt
删除:使用`d`命令进行删除。
例如,删除包含特定模式的行
sed '/pattern/d' file.txt
插入和追加:使用`i`命令和`a`命令进行插入和追加。
例如,在特定行前插入文本
sed '3i\inserted text' file.txt
替换标记(Substitution Flags): sed提供了一些替换命令的标记,用于修改替换行为。
有4种可用的替换标记:
替换命令在替换多行中的文本时能正常工作,但默认情况下它只替换每行中出现的第一处。 要让替换命令能够替换一行中不同地方出现的文本必须使用替换标记( substitution flag)。 替换标记会在替换命令字符串之后设置。
以下是这四个选项的示例:
1. 数字(n)- 替换第n处模式匹配的地方:
sed 's/pattern/new/2' file.txt
这将替换每行中第二次出现的 "pattern" 为 "new"。
2. 全局(g)- 替换所有匹配的文本:
sed 's/old/new/g' file.txt
这将替换每行中所有出现的 "old" 为 "new"。
3. 打印(p)- 原始行内容打印出来:
sed -n 's/pattern/new/p' file.txt
这将打印出每行中经过替换后匹配的行,在替换的行上会出现 "old" 替换为 "new"。
4. 写入文件(w file)- 将替换结果写入文件:
sed 's/old/new/gw output.txt' file.txt
这将将替换结果写入到名为 `output.txt` 的文件中。只有匹配和替换成功的行会被写入文件。
替换字符(Delimiter):在sed中,用于分隔替换命令中模式、替换字符串和替换标记的字符被称为替换字符,通常是斜杠(`/`),但也可以使用其他字符。这是因为有时你会在文本字符串中遇到一些不太方便在替换模式中使用的字符。
Linux中一个常见的例 子就是正斜线(`/`)。 由于正斜线通常用作字符串分隔符,因而如果它出现在了模式文本中的话,必须用反斜线来转 义。这通常会带来一些困惑和错误。 要解决这个问题, sed编辑器允许选择其他字符来作为替换命令中的字符串分隔符:
例如,使用井号(`#`)作为替换字符:
sed 's#old#new#g' file.txt
这样,将会替换每一行中匹配到的 "old" 为 "new"。
默认情况下,在sed编辑器中使用的命令会作用于文本数据的所有行。如果只想将命令作用于特定行或某些行,则必须用行寻址( line addressing)。
在sed编辑器中有两种形式的行寻址:
两种形式都使用相同的格式来指定地址:
也可以将特定地址的多个命令分组
下面将会演示如何在sed编辑器脚本中使用两种寻址方法。
在sed中,可以使用地址(address)和命令(command)的组合来指定要操作的行范围,并对这些行执行具体的命令
语法格式:
[address] command
当使用数字方式的行寻址时,可以用行在文本流中的行位置来引用。 sed编辑器会将文本流中 的第一行编号为1,然后继续按顺序为接下来的行分配行号。
在命令中指定的地址可以是单个行号,或是用起始行号、逗号以及结尾行号指定的一定区间范 围内的行。
以下是一些常见的数字方式行寻址的示例:
sed '2d' file.txt
这将删除文件中的第2行。
sed '1,3d' file.txt
这将删除文件中的第1行到第3行的内容。
sed '2,+2d' file.txt
这将删除文件中的第2行到第4行的内容。
sed '$d' file.txt
这将删除文件中的最后一行。
sed '2!d' file.txt
这将删除文件中的所有行,除了第2行。
sed '1~2d' file.txt
这将删除文件中的奇数行(从第1行开始,每隔1行)。
在sed中,可以使用文本模式过滤器(Text Mode Filters)来限定对特定文本范围的操作。文本模式过滤器指定了要操作的文本的起始和结束条件。
语法格式:
/pattern/command
必须用正斜线将要指定的pattern封起来。 sed编辑器会将该命令作用到包含指定文本模式的 行上。
以下是一些示例:
sed '/pattern1/,/pattern2/d' file.txt
这将删除从匹配到 "pattern1" 的行开始,到匹配到 "pattern2" 的行结束之间的所有行。
sed '/pattern1/,1d' file.txt
这将删除从匹配到 "pattern1" 的行开始到当前行(匹配到 "pattern1" 的行)之间的所有行。
sed '1,/pattern1/d' file.txt
这将删除从第1行到匹配到 "pattern1" 的行之间的所有行。
sed '/pattern1/,/pattern2/ s/old/new/g' file.txt
这将在匹配到 "pattern1" 到匹配到 "pattern2" 的行范围内,将所有的 "old" 替换为 "new"。
如果需要在单行上执行多条命令,可以用花括号将多条命令组合在一起。 sed编辑器会处理地 址行处列出的每条命令。
语法格式:
address {
command1
command2
command3
}
以下是一些示例:
1. 顺序执行多个命令:
sed 's/pattern1/new/; s/pattern2/old/' file.txt
这将先将匹配到的 "pattern1" 替换为 "new",然后将匹配到的 "pattern2" 替换为 "old"。
2. 条件执行命令:
sed '/pattern1/ s/old/new/; /pattern2/ s/foo/bar/' file.txt
这将在匹配到 "pattern1" 的行中将 "old" 替换为 "new",同时在匹配到 "pattern2" 的行中将 "foo" 替换为 "bar"。
3. 多个命令针对不同的行执行:
sed '1d; 4,6d; s/foo/bar/' file.txt
这将删除第1行、删除第4行到第6行的内容,然后将所有的 "foo" 替换为 "bar"。
4. 使用命令标签(label)和分支(branch):
sed '/pattern1/ {s/old/new/; b}; /pattern2/ {s/foo/bar/; q}' file.txt
这将在匹配到 "pattern1" 的行中将 "old" 替换为 "new",然后跳转到命令结束;如果匹配到 "pattern2" 的行,则将 "foo" 替换为 "bar" 后退出。
文本替换命令不是sed编辑器唯一的命令。如果需要删除文本流中的特定行,可以用删除命令。删除命令`d`名副其实,它会删除匹配指定寻址模式的所有行。使用该命令时要特别小心,如果你忘记加入寻址模式的话,流中的所有文本行都会被删除。
以下是一些删除行的示例:
1. 删除指定行数的行:
sed '2d' file.txt
这将删除文件中的第2行。
2. 删除匹配到指定模式的行:
sed '/pattern/d' file.txt
这将删除文件中所有匹配到正则表达式 "pattern" 的行。
3. 删除一定范围的行:
sed '2,5d' file.txt
这将删除文件中的第2行到第5行的内容。
4. 删除最后一行:
sed '$d' file.txt
这将删除文件中的最后一行。
5. 删除除了指定行号之外的所有行:
sed '2!d' file.txt
这将删除文件中除了第2行之外的所有行。
注,sed命令默认是将结果输出到标准输出。如果要修改原始文件,请使用`-i`选项。即:sed编辑器不会修改原始文件。你删除的行只是从sed编辑器的输出中消失了。原始文件仍然包含那些“删掉的”行。
跟其他编辑器类似, sed编辑器允许向数据流插入和附加文本行。
两个操作的区别:
它们不能在单个命令行上使用。你必须指定是要将行插入还是附加到另一行。格式如下:
sed '[address]command\
newline'
`new line`中的文本将会出现在sed编辑器输出中你指定的位置。
注:
在sed中,可以使用`i`命令来插入文本,并使用`a`命令来附加文本。
以下是一些插入和附加文本的示例:
1. 插入文本到指定行之前:
sed '2i\This is a new line' file.txt
这将在文件的第2行之前插入一行文本。
2. 附加文本到指定行之后:
sed '3a\This is an appended line' file.txt
这将在文件的第3行之后附加一行文本。
3. 插入多行文本到指定行之前:
sed '5i\
Line 1\
Line 2\
Line 3' file.txt
这将在文件的第5行之前插入三行文本。
4. 附加多行文本到指定行之后:
sed '6a\
Line 1\
Line 2\
Line 3' file.txt
这将在文件的第6行之后附加三行文本。
注:sed中,插入和附加文本时需要在命令后面使用`\`来表示新行。
修改( change)命令允许修改数据流中整行文本的内容。它跟插入和附加命令的工作机制一 样,你必须在sed命令中单独指定新行。
在sed中,要修改文本行,您可以使用`s`命令(substitute)。
以下是一些修改行的示例:
1. 修改指定行号的行:
sed '2s/old/new/' file.txt
这将在文件的第2行中将第一次出现的匹配到的 "old" 替换为 "new"。
2. 修改匹配到指定模式的行:
sed '/pattern/s/old/new/' file.txt
这将在文件中匹配到包含正则表达式 "pattern" 的每一行中,将第一次出现的匹配到的 "old" 替换为 "new"。
3. 修改多个匹配到指定模式的行:
sed '/pattern/s/old/new/g' file.txt
这将在文件中匹配到包含正则表达式 "pattern" 的每一行中,将所有匹配到的 "old" 都替换为 "new"。
4. 修改指定行范围内的行:
sed '2,5s/old/new/' file.txt
这将在文件的第2行到第5行之间的每一行中,将第一次出现的匹配到的 "old" 替换为 "new"。
注:在替换命令(s命令)中,您可以使用g标志来指示替换所有匹配项,而不仅仅是第一个匹配项。
转换( transform)命令(y)是唯一可以处理单个字符的sed编辑器命令。
转换命令格式如下。
[address]y/inchars/outchars/
转换命令会对inchars和outchars值进行一对一的映射。 inchars中的第一个字符会被转换为outchars中的第一个字符,第二个字符会被转换成outchars中的第二个字符。这个映射过程会一直持续到处理完指定字符。如果inchars和outchars的长度不同,则sed编辑器会产生一 条错误消息。
以下是一个转换命令的示例:
sed 'y/abc/def/' file.txt
这会将文件中的每个字符a替换为d,b替换为e,c替换为f。换句话说,它将字符序列abc转换为def。
注:转换命令是基于字符级别的替换,而不是基于字符串的替换。它逐个字符进行转换,并且大小写敏感。
除了使用p标记和替换命令显示sed编辑器修改过的行。另外有3个命令也能用来打印数据流中 的信息:
打印指定的行:
sed -n '2p' file.txt
这将打印文件中的第2行。
打印所有的行:
sed -n 'p' file.txt
这将打印文件中的所有行。
注:使用`-n`选项可以禁止sed的默认行为(即打印每一行),从而只按需打印或列出行。
`example.txt`的文本文件,内容如下:
This is line 1.
This is line 2.
This is line 3.
This is line 4.
This is line 5.
要仅打印包含文本`line 3`的行,可以使用以下命令:
sed -n '/line 3/p' example.txt
输出将是:
This is line 3.
在这个例子中,使用`-n`选项告诉`sed`不要打印任何行。然后使用`/line 3/p`将匹配包含文本`line 3`的行,并使用`p`命令打印这些行。
sed '=' file.txt
这将打印文件中的每一行,并在每一行前面添加行号。
注:利用-n选项,你就能让sed编辑器只显示包含匹配文本模式的行的行号和文本。
`example.txt`的内容如下:
This is line 1.
This is line 2.
This line contains the pattern.
This is line 4.
This is line 5.
如果我们使用以下命令:
sed -n '/pattern/=' example.txt
输出将是:
3
在这个例子中,使用`-n`选项告诉`sed`不要打印任何行。然后使用`/pattern/=`将匹配包含文本模式的行,并使用`=`命令打印行号。
列出(list)命令(l)可以打印数据流中的文本和不可打印的ASCII字符。任何不可打印字符要么在其八进制值前加一个反斜线,要么使用标准C风格的命名法(用于常见的不可打印字符),比如\t,来代表制表符。
下面是一个示例,展示了如何在`sed`中使用`l`命令来打印文本和不可打印字符:
echo -e "This is a\ttab\tcharacter." | sed -n 'l'
输出将是:
This is a\011tab\011character.$
在这个示例中,`echo -e`命令用于将转义序列(例如`\t`)转换为其相应的字符。然后,使用`sed -n 'l'`命令来打印文本和不可打印字符。制表符被表示为`\011`,表示为`tab`。
注:上述示例中的`echo`命令是使用bash特定的`-e`选项来处理转义序列的扩展。使用其他shell或环境时,可能需要使用不同的方法来处理转义序列
w命令用来向文件写入行。该命令的格式如下:
[address]w filename
`filename`可以使用相对路径或绝对路径,但不管是哪种,运行sed编辑器的用户都必须有文件的写权限。地址可以是sed中支持的任意类型的寻址方式,例如单个行号、文本模式、行区间或文本模式。
示例:
我们有一个名为`input.txt`的文件,内容如下:
Line 1
Line 2
Line 3
我们可以使用以下命令将其中匹配的行(例如,匹配以"Line"开头的行)写入到名为`output.txt`的输出文件中:
sed -n '/^Line/w output.txt' input.txt
执行这个命令后,将会生成一个名为`output.txt`的文件,其中包含以下内容:
Line 1
Line 2
Line 3
之前了解了如何在sed命令行上向数据流中插入或附加文本。读取(read)命令(r)允许你将一个独立文件中的数据插入到数据流中。读取命令的格式如下:
[address]r filename
`filename`参数指定了数据文件的绝对路径或相对路径。你在读取命令中使用地址区间,只能指定单独一个行号或文本模式地址。 sed编辑器会将文件中的文本插入到指定地址后。
示例:
我们有一个名为`insert.txt`的文件,内容如下:
This is the inserted text.
现在我们有一个名为`input.txt`的文件,内容如下:
Line 1
Line 2
Line 3
我们可以使用`r`命令将`insert.txt`文件中的内容插入到`input.txt`文件的特定位置(例如,在第2行之后)。使用以下命令:
sed '2r insert.txt' input.txt
执行此命令后,将会在`input.txt`文件中第2行的下方插入`insert.txt`文件的内容,结果如下:
Line 1
Line 2
This is the inserted text.
Line 3
读取命令的另一个很酷的用法是和删除命令配合使用:
利用另一个文件中的数据来替换文件中的占位文本。
我们有一个名为`template.txt`的文件,其中保存着一个套用信件的文本,内容如下:
Dear [Name],
Thank you for your interest in our product. We are pleased to inform you that...
Best regards,
[Your Name]
我们还有一个名为`data.txt`的文件,内容如下:
John Doe
Jane Smith
现在,我们希望将`template.txt`文件中的`[Name]`替换为`data.txt`文件中的每一行文本,生成个性化的信件。可以使用以下命令:
sed 's/\[Name\]/{r data.txt
d}' template.txt
执行此命令后,将会输出替换后的个性化信件:
Dear John Doe,
Thank you for your interest in our product. We are pleased to inform you that...
Best regards,
[Your Name]
然后,再次执行相同的命令,但将`[Name]`替换为`Jane Smith`,输出另一封个性化的信件:
Dear Jane Smith,
Thank you for your interest in our product. We are pleased to inform you that...
Best regards,
[Your Name]
在上述命令中,我们使用了`r`命令来读取`data.txt`文件的内容,并使用`d`命令来删除原始的占位文本`[Name]`。通过多次执行`sed`命令,可以对每一行数据都实现相应的替换。
注:这个例子`data.txt`中的每一行文本都对应着一个个性化的值,且按顺序替换。如果`data.txt`中的行数与占位文本的数量不匹配,可能会导致替换结果不准确。
虽然sed编辑器是非常方便自动修改文本文件的工具,但其也有自身的限制。通常你需要一个用来处理文件中的数据的更高级工具,它能提供一个类编程环境来修改和重新组织文件中的数据。这正是awk能够做到的。
awk程序是Unix中的原始awk程序的GNU版本。 awk程序让流编辑迈上了一个新的台阶,它提供了一种编程语言而不只是编辑器命令。在awk编程语言中,你可以做下面的事情:
awk程序的报告生成能力通常用来从大文本文件中提取数据元素,并将它们格式化成可读的报 告。其中最完美的例子是格式化日志文件。在日志文件中找出错误行会很难,awk程序可以让你从日志文件中过滤出需要的数据元素,然后你可以将其格式化,使得重要的数据更易于阅读。
awk的强大之处在于程序脚本。可以写脚本来读取文本行的数据,然后处理并显示数据,创建任何类型的输出报告。
`awk` 命令的基本语法格式:
awk options program file
常用选项:
下面是一些常用的 `awk` 语法示例:
1. 打印整行:
awk '{ print }' 文件名
2. 指定字段分隔符打印指定的字段:
awk -F, '{ print $1, $3 }' 文件名
3. 根据条件筛选行并打印指定字段:
awk -F, '$2 > 100 { print $1, $3 }' 文件名
4. 计算并打印某个字段的总和:
awk -F, '{ sum += $2 } END { print sum }' 文件名
5. 使用自定义变量进行计算和打印:
awk -F, '{ total = $2 * 2; print "Total: " total }' 文件名
6. 使用脚本文件进行复杂的数据处理:
awk -f script.awk 文件名
awk程序脚本用一对花括号来定义。你必须将脚本命令放到两个花括号( {})中。如果你错误 地使用了圆括号来包含awk脚本,就会得到一条类似于下面的错误提示。
[root@localhost ~]# awk '(print "Hello World!"}'
awk: cmd. line:1: (print "Hello World!"}
awk: cmd. line:1: ^ syntax error
由于awk命令行假定脚本是单个文本字符串,你还必须将脚本放到单引号中。下面的例子在命令行上指定了一个简单的awk程序脚本:
awk '{print $1, $3}' data.txt
这个示例将会打印出 `data.txt` 文件中的每一行的第一个和第三个字段。
awk的主要特性之一是其处理文本文件中数据的能力。它会自动给一行中的每个数据元素分配 一个变量。
默认情况下, awk会将如下变量分配给它在文本行中发现的数据字段:
在文本行中,每个数据字段都是通过字段分隔符划分的。 awk在读取一行文本时,会用预定义 的字段分隔符划分每个数据字段。 awk中默认的字段分隔符是任意的空白字符(例如空格或制表符)。
例如,如果数据文件使用逗号作为字段分隔符,你可以使用`-F`选项将逗号指定为分隔符。
awk -F ',' '{print $1}' data.txt
上述示例中,我们使用的是名为 `data.txt` 的数据文件,其中字段是用逗号分隔的。`-F ','` 将逗号指定为字段分隔符,然后`{print $1}` 会打印每一行的第一个字段。
注意,逗号需要用单引号括起来,以防止Shell将其解释为命令的分隔符。
示例:
awk '{print $2}' data.txt
这个示例将会打印出 `data.txt` 文件中的每一行的第二个字段。
如果一种编程语言只能执行一条命令,那么它不会有太大用处。 awk编程语言允许你将多条命令组合成一个正常的程序。要在命令行上的程序脚本中使用多条命令,只要在命令之间放个分号即可。
使用花括号`{}`将多个命令组合在一起。
示例:
awk '{print $1; print $3}' data.txt
这个示例将会打印出 `data.txt` 文件中的每一行的第一个字段,然后打印第三个字段。
跟sed编辑器一样, awk编辑器允许将程序存储到文件中,然后再在命令行中使用`-f`选项指定该文件名并引用。
示例:
awk -f script.awk data.txt
这个示例将会执行 `script.awk` 文件中的AWK程序脚本,并对 `data.txt` 文件进行处理。
注意, awk程序在引用变量值时并未像shell脚本一样使用美元符。
awk还允许指定程序脚本何时运行。默认情况下,awk会从输入中读取一行文本,然后针对行的数据执行程序脚本。有时可能需要在处理数据前运行脚本,比如为报告创建标题。 BEGIN关键字就是用来做这个的。它会强制awk在读取数据前执行BEGIN关键字后指定的序脚本。
示例:
awk 'BEGIN{print "标题行"} {print $1}' data.txt
这个示例将会在处理 `data.txt` 文件前先打印出 "标题行",然后再打印出每一行的第一个字段。
与BEGIN关键字类似, END关键字允许你指定一个程序脚本, awk会在读完数据后执行它。
示例:
awk '{sum += $1} END{print "总和:", sum}' data.txt
这个示例将会计算 `data.txt` 文件中每一行的第一个字段的总和,并在处理完所有数据后打印出 "总和:" 以及计算得到的总和值。
示例:
awk -F ',' '
BEGIN {
print "开始处理数据..."
}
{
print $1
}
END {
print "数据处理完成。"
}' data.txt
在上述示例中,我们使用了三个不同的块来定义AWK程序:
- BEGIN块:在处理数据之前执行的代码块,在开始处理数据之前可以在这里做一些初始化操作。
- 主逻辑块:对每一行数据执行的代码块,这里我们打印了第一个字段的值。
- END块:在处理数据之后执行的代码块,可以在这里进行一些总结性的操作。
执行以上命令时,首先会显示 "开始处理数据...",然后对每一行数据打印第一个字段的值,最后显示 "数据处理完成。"
所有编程语言共有的一个重要特性是使用变量来存取值。awk编程语言支持两种不同类型的变 量:
awk有一些内建变量。这些变量存放用来处理数据文件中的数据字段和记录的信息。你也可以 在awk程序里创建你自己的变量。下面将带你了解如何在awk程序里使用变量。
1)使用内建变量
对于内建变量,Awk 提供了一些预定义的变量,可以直接在脚本中使用。其中,最常用的是`$0`,它表示当前记录的整个行。其他内建变量如下:
例如,我们有一个文本文件`data.txt`,内容如下:
Alice 25
Bob 30
Charlie 35
我们可以使用内建变量来处理这个文件:
awk '{print "Name: " $1 ", Age: " $2}' data.txt
输出结果为:
Name: Alice, Age: 25
Name: Bob, Age: 30
Name: Charlie, Age: 35
1.字段与记录分隔符变量
在Awk中,你可以自定义字段分隔符(FS)和记录分隔符(RS),它们分别用于解析输入数据文件的字段和记录。默认情况下,字段分隔符是空白字符(空格或制表符),记录分隔符是换行符。
你可以通过内建变量`FS`和`RS`来设置字段分隔符和记录分隔符的值。下表列出了这些内建变量。
变量 | 描述 |
---|---|
FIELDWIDTHS | 由空格分隔的一列数字,定义了每个数据字段确切宽度 |
FS | 输入字段分隔符 |
RS | 输入记录分隔符 |
OFS | 输出字段分隔符 |
ORS | 输出记录分隔符 |
例如,我们有一个包含逗号分隔的数据文件`data.txt`,内容如下:
Alice,25
Bob,30
Charlie,35
我们可以使用`FS`变量来指定字段分隔符为逗号:
awk 'BEGIN {FS=","} {print "Name: " $1 ", Age: " $2}' data.csv
输出结果为:
Name: Alice, Age: 25
Name: Bob, Age: 30
Name: Charlie, Age: 35
同样地,你也可以使用`RS`变量来指定记录分隔符。
例如,我们有一个以空行分隔的文本文件`data.txt`,内容如下:
Alice
25Bob
30Charlie
35
我们可以使用`RS`变量来指定记录分隔符为两个连续的换行符:
awk 'BEGIN {RS="\n\n"} {print "Name: " $1}' data.txt
输出结果为:
Name: Alice
Name: Bob
Name: Charlie
2. 数据变量
除了字段和记录分隔符变量外, awk还提供了其他一些内建变量来帮助你了解数据发生了什么 变化,并提取shell环境的信息。下表列出了awk中的其他内建变量。
下面是每个内建变量的说明和示例:
1. `ARGC`:当前命令行参数的个数。
示例:
awk 'BEGIN {print ARGC}' file.txt
输出结果是命令行参数的个数。
2. `ARGIND`:当前文件在`ARGV`数组中的位置。
示例:
awk 'BEGIN {print ARGIND}' file1.txt file2.txt
输出结果是当前文件在`ARGV`数组中的位置。
3. `ARGV`:包含命令行参数的数组。
示例:
awk 'BEGIN {for (i=1; i<=ARGC; i++) print ARGV[i]}' file.txt
输出结果是`ARGV`数组中的各个元素,即命令行参数。
4. `CONVFMT`:数字的转换格式,默认为`%.6g`。
示例:
awk 'BEGIN {CONVFMT = "%.2f"; printf "%a\n", 3.14159}' file.txt
输出结果是按照`CONVFMT`指定的格式转换数字。
5. `ENVIRON`:当前shell环境变量及其值组成的关联数组。
示例:
awk 'BEGIN {print ENVIRON["HOME"]}' file.txt
输出结果是当前shell的`HOME`环境变量的值。
6. `ERRNO`:当读取或关闭输入文件发生错误时的系统错误号。
示例:
awk 'BEGIN {if (getline < "nonexistent.txt" == -1) print ERRNO}' file.txt
输出结果是读取不存在的文件时的错误号。
7. `FILENAME`:用作awk输入数据的数据文件的文件名。
示例:
awk '{print FILENAME, $0}' file1.txt file2.txt
输出结果是当前正在处理的文件名和文件中的每一行。
8. `FNR`:当前数据文件中的数据行数。
示例:
awk '{print FNR, $0}' file.txt
输出结果是当前数据文件中每一行的行号和行内容。
9. `IGNORECASE`:设为非零值时,忽略awk命令中出现的字符串的字符大小写。
示例:
awk 'BEGIN {IGNORECASE = 1} /pattern/ {print $0}' file.txt
输出结果是忽略大小写匹配到的包含`pattern`的行。
10. `NF`:数据文件中的字段总数。
示例:
awk '{print NF, $0}' file.txt
输出结果是当前数据文件中每一行的字段数和行内容。
11. `NR`:已处理的输入记录数。
示例:
awk '{print NR, $0}' file.txt
输出结果是已处理的输入记录数和每一行的内容。
12. `OFMT`:数字的输出格式,默认为`%.6g`。
示例:
awk 'BEGIN {OFMT = "%.2f"; print 3.14159}' file.txt
输出结果是按照`OFMT`指定的格式输出数字。
13. `RLENGTH`:由`match`函数所匹配的子字符串的长度。
示例:
awk 'BEGIN {str = "Hello, World!"; match(str, /World/); print RLENGTH}' file.txt
输出结果是匹配到的子字符串的长度。
14. `RSTART`:由`match`函数所匹配的子字符串的起始位置。
示例:
awk 'BEGIN {str = "Hello, World!"; match(str, /World/); print RSTART}' file.txt
输出结果是匹配到的子字符串的起始位置。
2)自定义变量
跟其他典型的编程语言一样, awk允许你定义自己的变量在程序代码中使用。 awk自定义名可以是任意数目的字母、数字和下划线,但不能以数字开头。重要的是,要记住awk变量区分大小写。
1.在脚本中给变量赋值
除了内建变量,你也可以在 awk 脚本中自定义变量。在脚本中给变量赋值可以使用`变量名=值`的语法。例如:
awk '{
name = $1
age = $2
print "Name: " name ", Age: " age
}' data.txt
输出结果同样为:
Name: Alice, Age: 25
Name: Bob, Age: 30
Name: Charlie, Age: 35
2.在命令行上给变量赋值
也可以用awk命令行来给程序中的变量赋值。这允许你在正常的代码之外赋值,即时改变变量 的值。
使用命令行参数来定义变量值会有一个问题。在你设置了变量后,这个值在代码的BEGIN部分不可用。
可以用-v命令行参数来解决这个问题。它允许你在BEGIN代码之前设定变量。在命令行上,-v命令行参数必须放在脚本代码之前。
awk -v name="Alice" -v age=25 '{
print "Name: " name ", Age: " age
}' data.txt
输出结果同样为:
Name: Alice, Age: 25
Name: Alice, Age: 25
Name: Alice, Age: 25
为了在单个变量中存储多个值,许多编程语言都提供数组。 awk编程语言使用关联数组提供数组功能。
关联数组跟数字数组不同之处在于它的索引值可以是任意文本字符串。你不需要用连续的数字来标识数组中的数据元素。相反,关联数组用各种字符串来引用值。每个索引字符串都必须能够唯一地标识出赋给它的数据元素。
1)定义数组变量
可以用标准赋值语句来定义数组变量。数组变量赋值的格式如下:
var[index] = element
其中var是变量名, index是关联数组的索引值, element是数据元素值。
例如,下面的示例演示了如何定义一个名为 fruits
的数组变量,并为其指定一些值:
awk 'BEGIN { fruits["apple"] = 5; fruits["orange"] = 3; fruits["banana"] = 2; print fruits["apple"] }'
输出结果是 `5`,表示 `fruits["apple"]` 的值为 `5`。
2)遍历数组变量
关联数组变量的问题在于你可能无法知晓索引值是什么。跟使用连续数字作为索引值的数字数 组不同,关联数组的索引可以是任何东西。 如果要在awk中遍历一个关联数组,可以用for语句的一种特殊形式,并使用 `in` 关键字来获取数组的键值。
示例代码如下所示:
awk 'BEGIN { fruits["apple"] = 5; fruits["orange"] = 3; fruits["banana"] = 2; for (fruit in fruits) print fruit, fruits[fruit] }'
输出结果为:
apple 5
orange 3
banana 2
这个示例遍历了 `fruits` 数组变量,并打印出每个水果的名称和对应的数量。
注意,索引值不会按任何特定顺序返回,但它们都能够指向对应的数据元素值。明白这点很重 要,因为你不能指望着返回的值都是有固定的顺序,只能保证索引值和数据值是对应的。
3)删除数组变量
从关联数组中删除数组索引要用一个特殊的命令。
语法格式如下:
delete array[index]
删除命令会从数组中删除关联索引值和相关的数据元素值。
示例代码如下所示:
awk 'BEGIN { fruits["apple"] = 5; fruits["orange"] = 3; fruits["banana"] = 2; delete fruits["orange"]; print fruits["orange"] }'
输出结果是空,表示 `fruits["orange"]` 已被删除。
awk程序支持多种类型的匹配模式来过滤数据记录,这一点跟sed编辑器大同小异。
BEGIN和 END关键字是用来在读取数据流之前或之 后执行命令的特殊模式。
类似地,你可以创建其他模式在数据流中出现匹配数据时执行一些命 令。
1)正则表达式
前面介绍了如何将正则表达式用作匹配模式。可以用基础正则表达式( BRE)或扩展正则表达式( ERE)来选择程序脚本作用在数据流中的哪些行上。在使用正则表达式时,正则表达式必须出现在它要控制的程序脚本的左花括号前。
示例:
awk '/pattern/ {print}' file.txt
这个示例将匹配包含"pattern"的行,并打印出匹配到的行。
注:当试图匹配某个数据字段中的特定数据时,这些数据又出现在其他数据字段中。如果需要用正则表达式匹配某个特定的数据实例,应该使用匹配操作符。
2)匹配操作符
匹配操作符( matching operator)允许将正则表达式限定在记录中的特定数据字段。匹配操作符是波浪线( ~)。可以指定匹配操作符、数据字段变量以及要匹配的正则表达式。例如,可以使用"=="进行相等比较,"~"进行模式匹配,"!~"进行模式不匹配等。这些操作符可以用于模式中来进行更灵活的逻辑匹配。
示例:
awk '$1 == "apple" {print}' file.txt
这个示例将匹配第一个字段等于"apple"的行,并打印出匹配到的行。
3)数学表达式
除了正则表达式,你也可以在匹配模式中用数学表达式。这个功能在匹配数据字段中的数字值时非常方便。举个例子,如果你想显示所有属于root用户组(组ID为0)的系统用户,可以用这个脚本。
[root@localhost ~]# awk -F: '$4 == 0{print $1}' /etc/passwd
这段脚本会查看第四个数据字段含有值0的记录。在这个Linux系统中,有五个用户账户属于 root用户组。
可以使用任何常见的数学比较表达式。
也可以对文本数据使用表达式,但必须小心。跟正则表达式不同,表达式必须完全匹配。数据 必须跟模式严格匹配。
示例:
awk '$2 > 50 {print}' file.txt
这个示例将匹配第二个字段大于50的行,并打印出匹配到的行。
在awk中,你可以使用一些结构化的命令来实现条件控制和循环操作。
1)if 语句
awk编程语言支持标准的if-then-else格式的if语句。你必须为if语句定义一个求值的条件, 并将其用圆括号括起来。如果条件求值为TRUE,紧跟在if语句后的语句会执行。如果条件求 值为FALSE,这条语句就会被跳过。可以用这种格式:
if (condition) {
# 在条件为真时执行的代码块
} else {
# 在条件为假时执行的代码块
}
示例:
awk '{ if ($1 == "apple") print "Fruit: " $1; else print "Not a fruit: " $1; }' file.txt
这个示例根据第一个字段的值来判断是否是水果,然后打印相应的信息。
可以在单行上使用else子句,但必须在if语句部分之后使用分号。但这样格式更紧凑,但也更难理解。所以不是很推荐这种写法。
注意,不能弄混if语句的花括号和用来表示程序脚本开始和结束的花括号。如果弄混了,awk程序能够发现丢失了花括号,并产生一条错误消息。
2)while 语句
while语句用于在条件为真时重复执行一段代码块。
基本语法如下:
while (condition) {
# 循环执行的代码块
}
示例:
awk 'BEGIN { i = 1; while (i <= 5) { print i; i++; } }'
这个示例使用while循环输出数字1到5。
awk编程语言支持在while循环中使用break语句和continue语句,允许你从循环中跳出。
示例:
awk 'BEGIN { i = 1; while (i <= 10) { if (i == 5) break; print i; i++; } }'
在这个示例中,当`i`等于5时,`break`语句被执行,终止了循环。
示例:
awk 'BEGIN { for (i = 1; i <= 5; i++) { if (i == 3) continue; print i; } }'
在这个示例中,当`i`等于3时,`continue`语句被执行,跳过当前迭代的剩余代码,继续进行下一次迭代。
注:`break`和`continue`语句只在最内层的循环中起作用,如果有嵌套循环,它们只会对内层循环起作用。
3)do-while 语句
do-while语句与while语句类似,但是它会先执行一次代码块,然后再根据条件判断是否继续执行。基本语法如下:
do {
# 循环执行的代码块
} while (condition)
这种格式保证了语句会在条件被求值之前至少执行一次。当需要在求值条件前执行语句时,这个特性有时非常方便。
示例:
awk 'BEGIN { i = 1; do { print i; i++; } while (i <= 5) }'
这个示例使用do-while循环输出数字1到5。
4)for 语句
for语句用于指定循环的初始条件、循环条件和每次迭代后的操作。
基本语法如下:
for (initialization; condition; increment) {
# 循环执行的代码块
}
示例:
awk 'BEGIN { for (i = 1; i <= 5; i++) { print i; } }'
这个示例使用for循环输出数字1到5。
这些结构化命令使你能够根据条件执行不同的操作或者在特定条件下进行循环操作,从而提供了更多的灵活性和控制能力。你可以根据具体需求选择适合的结构化命令来编写Awk程序。
awk编程语言提供了不少内置函数,可进行一些常见的数学、字符串以及时间函数运算。你可以在awk程序中利用这些函数来减少脚本中的编码工作。
1)数学函数
如果你有过其他语言的编程经验,可能就会很熟悉在代码中使用内建函数来进行一些常见的数 学运算。 awk编程语言不会让那些寻求高级数学功能的程序员失望。
函数 | 描述 |
---|---|
atan2(x, y) | x/y的反正切, x和y以弧度为单位 |
cos(x) | x的余弦,x以弧度为单位 |
exp(x) | x的指数函数 |
int(x) | x的整数部分,取靠近零一侧的值 |
log(x) | x的自然对数 |
rand( ) | 比0大比1小的随机浮点值 |
sin(x) | x的正弦, x以弧度为单位 |
sqrt(x) | x的平方根 |
srand(x) | 为计算随机数指定一个种子值 |
虽然数学函数的数量并不多,但awk提供了标准数学运算中要用到的一些基本元素。
除了标准数学函数外, awk还支持一些按位操作数据的函数。
位操作函数在处理数据中的二进制值时非常有用。
示例:
awk 'BEGIN { print sqrt(16) }'
示例:
awk 'BEGIN { print int(3.14) }'
示例:
awk 'BEGIN { print rand() }'
注:在使用一些数学函数时要小心,因为awk语言对于它能够处理的数值有一个限定区间。如果超 出了这个区间,就会得到一条错误消息。
2)字符串函数
awk编程语言还提供了一些可用来处理字符串值的函数,如表所示。
示例:
awk 'BEGIN { string = "Hello, World!"; print length(string) }'
示例:
awk 'BEGIN { string = "Hello, World!"; print substr(string, 1, 5) }'
示例:
awk 'BEGIN { string = "Hello, World!"; print index(string, "World") }'
3)时间函数
awk编程语言包含一些函数来帮助处理时间值,如表所示。
时间函数常用来处理日志文件,而日志文件则常含有需要进行比较的日期。通过将日期的文本 表示形式转换成epoch时间(自1970-01-01 00:00:00 UTC到现在的秒数),可以轻松地比较日期。
示例:
awk 'BEGIN { print systime() }'
示例:
awk 'BEGIN { print strftime("%Y-%m-%d %H:%M:%S", systime()) }'
这些只是awk提供的一小部分内建函数。你可以根据自己的需要,在awk程序中使用适当的内建函数来处理数据。
1)定义函数
你可以使用`function`关键字来定义一个函数,并指定函数的名称和参数列表。在函数体内部,你可以执行任意的操作。
在AWK中定义函数可以使用如下语法:
function function_name(parameters) {
# 函数体
# 可以包含多个语句
# 可以使用参数
# 可以返回值
}
例如,我们定义一个函数`calculate_sum`,接收两个参数`num1`和`num2`,用于计算它们的和并返回结果:
function calculate_sum(num1, num2) {
return num1 + num2;
}
你可以在AWK脚本的其他地方调用这个函数,比如在`main`函数中:
function main() {
result = calculate_sum(3, 5);
print "Sum: " result;
}
2)使用自定义函数:
在定义函数时,它必须出现在所有代码块之前(包括BEGIN代码块)。乍一看可能有点怪异, 但它有助于将函数代码与awk程序的其他部分分开。
你可以在awk程序的任何地方调用自定义函数,并传递相应的参数。
示例:
假设我们有一个包含学生成绩的CSV文件,每行有学生的姓名和分数,用逗号分隔。我们希望计算每个学生的平均分并输出。
首先,创建一个名为`calculate_average`的函数,它接收一个包含分数的数组作为参数,并返回平均分。
function calculate_average(scores) {
sum = 0;
for (i = 1; i <= length(scores); i++) {
sum += scores[i];
}
return sum / length(scores);
}
接下来,编写主逻辑部分,读取CSV文件并对每个学生的成绩进行处理。
BEGIN {
FS = ",";
}
{
student_name = $1;
split($2, scores, " ");
average = calculate_average(scores);
print "Student: " student_name;
print "Average score: " average;
}
保存脚本为`script.awk`,然后运行命令`awk -f script.awk students.csv`,其中`students.csv`是包含学生成绩的CSV文件。
`students.csv`文件的内容如下:
Alice,90 95 85
Bob,80 75 88 92
运行脚本后,你将得到以下输出:
Student: Alice
Average score: 90
Student: Bob
Average score: 83.75
3)创建函数库
显而易见,每次使用函数都要重写一遍并不美妙。不过, awk提供了一种途径来将多个函数放 到一个库文件中,这样你就能在所有的awk程序中使用了。
创建过程如下:
1. 创建一个名为`library.awk`的文件,其中包含所有的自定义函数定义。
# library.awk
function calculate_average(scores) {
sum = 0;
for (i = 1; i <= length(scores); i++) {
sum += scores[i];
}
return sum / length(scores);
}
function find_max_value(arr) {
max = arr[1];
for (i = 2; i <= length(arr); i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
# 添加更多的函数定义
2. 在需要使用这些自定义函数的AWK脚本中,使用`@include`指令包含函数库文件。
# main.awk
@include "library.awk"
# 调用自定义函数
scores = [90, 95, 85];
average = calculate_average(scores);
print "Average score: " average;
# 调用另一个自定义函数
values = [15, 8, 20, 12];
max_value = find_max_value(values);
print "Max value: " max_value;
# 添加自己的逻辑
3. 保存主逻辑脚本为`main.awk`。
4. 运行AWK命令来执行`main.awk`脚本,例如:`awk -f main.awk`。
通过将自定义函数放入一个单独的函数库文件中,并使用`@include`指令将其包含在需要的脚本中,你就可以在多个AWK脚本中重复使用这些函数。