sed & awk 第二版学习(一)—— sed 与 awk 基本操作

2024-08-21 14:13:16 浏览数 (2)

一、使用 sed

1. 命令行上指定多个指令

创建名为 list 的文件内容如下:

代码语言:javascript复制
John Daggett, 341 King Road, Plymouth MA
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Eric Adams, 20 Post Road, Sudbury MA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston MA

有 3 种方式可以在 sed 命令行上指定多个指令: 1. 用分号分隔指令。

代码语言:javascript复制
sed 's/ MA/, Massachusetts/; s/ PA/, Pennsylvania/' list

2. 在每个指令前放置 -e。

代码语言:javascript复制
sed -e 's/ MA/, Massachusetts/' -e 's/ PA/, Pennsylvania/' list

3. 使用 bash 的分行指令。在输入单引号后按回车键,就会出现多行输入的提示符(>)。

代码语言:javascript复制
$ sed '
> s/ MA/, Massachusetts/
> s/ PA/, Pennsylvania/
> s/ CA/, California/' list

上面的例子中修改了 5 行,但所有的行都被显示出来,并且不会真正改变输入文件。

并不是在任何情况下都需要将指令用单引号括起来,但应该养成这个习惯。使用单引号可以阻止 shell 解释 sed 指令中的特殊字符或空格(shell 使用空格决定提交给程序的独立的参数,特殊的 shell 字符在调用之前被展开)。

如果输入了不完整的语法,sed 通常会显示任何它不能执行的行,并清晰地提示命令所发生的错误。例如下面的情况,少了标记搜索和替换命令末尾的斜杠:

代码语言:javascript复制
$ sed -e 's/MA/Massachusetts' list
sed:-e 表达式 #1,字符 18:未终止的“s”命令

2. 使用脚本文件

在命令行上输入较长的编辑脚本是不实际的,这就是最好创建包含编辑指令的脚本文件的原因。脚本文件只是一系列依次执行的简单的 sed 命令。这种形式使用 -f 选项指定命令行上的脚本文件的名字,格式如下:

代码语言:javascript复制
sed -f scriptfile file

将要执行的所有编辑命令都放置在脚本文件中。创建名为 sedscr 的脚本文件内容如下:

代码语言:javascript复制
s/ MA/, Massachusetts/
s/ PA/, Pennsylvania/
s/ CA/, California/
s/ VA/, Virginia/
s/ OK/, Oklahoma/

下面的命令读取 sedscr 中的所有替换命令,并将这些命令应用于输入文件 list 中的每一行:

代码语言:javascript复制
$ sed -f sedscr list
John Daggett, 341 King Road, Plymouth, Massachusetts
Alice Ford, 22 East Broadway, Richmond, Virginia
Orville Thomas, 11345 Oak Bridge Road, Tulsa, Oklahoma
Terry Kalkas, 402 Lans Road, Beaver Falls, Pennsylvania
Eric Adams, 20 Post Road, Sudbury, Massachusetts
Hubert Sims, 328A Brook Road, Roanoke, Virginia
Amy Wilde, 334 Bayshore Pkwy, Mountain View, California
Sal Carpenter, 73 6th Street, Boston, Massachusetts

再说一次,显示在屏幕上的结果是临时的,输入文件中没有发生改变。

3. 保存输出

将 sed 的输出重定向到另一个程序中,能够捕获文件中的输出。要完成这项工作需要在一个文件名后面指定一个 shell 的 I/O 重定向符号。例如:

代码语言:javascript复制
$ sed -f sedscr list > newlist

不要将输出重定向到正在编辑的文件中,否则就会使它变成乱码(“>”重定向操作符在 shell 做任何其它事情之前截取文件)。如果想用输出文件取代输入文件,可以采用 mv 命令并将它作为单独的步骤来处理,但首先要确保编辑的脚本是正确的!更简单的做法是使用 -i 选项直接修改输入文件,但建议先将输入文件做备份。

4. 阻止输入行的自动显示

sed 的默认操作是输出每个输入行。 -n 选型可以阻止自动输出。当指定该选项时,每个要生成输出的指令都必须包含打印命令 p。下面的示例只会显示受命令影响的行:

代码语言:javascript复制
$ sed -n -e 's/MA/Massachusetts/p' list
John Daggett, 341 King Road, Plymouth Massachusetts
Eric Adams, 20 Post Road, Sudbury Massachusetts
Sal Carpenter, 73 6th Street, Boston Massachusetts

5. 混合选项(POSIX)

通过合并命令行上的 -e 和 -f 选项可以构建一个脚本,该脚本是所有命令按命令的给出顺序组合起来的。POSIX 标准明确地要求这种特征。下面汇总了三个 sed 的常用命令行选项:

  • -e:执行随后的指令。
  • -f:后跟脚本的文件名。
  • -n:阻止输入行的自动输出。

二、使用 awk

与 sed 相似,awk 为每个输入行执行一套指令。可以在命令行上指定指令或创建脚本文件。

1. 运行 awk

命令行的语法是:

代码语言:javascript复制
awk 'instructions' files

每次从一个或多个文件中读入一行,或从标准输入中读入一行。指令必须包含在单引号中,从而与 shell 区分开(指令几乎总是包含大括号、/ 或美元符,shell 将它们解释为特殊符号)。可以用与 sed 相同的方式输入多个指令:用分号分隔或使用 bash 的多行输入功能。

awk 程序通常被放置在可以对它们进行测试和修改的文件中。用脚本文件调用 awk 的语法如下,-f 选项的工作方式与在 sed 中相同:

代码语言:javascript复制
awk -f script files

在 sed 和 awk 中,每个指令都包括两部分:模式和过程。模式是由斜杠(/)分隔的正则表达式,过程指定一个或多个将被执行的动作。但两者的过程本身有很大不同, sed 像编辑器而 awk 更像一种程序设计语言。语句和函数取代了使用一两个字符组成的命令序列。例如,使用 print 语句打印表达式的值或打印当前输入行的内容。

通常情况下,awk 将每个输入行解释为一条记录,而将那一行上的每个单词(由空格或制表符分隔)解释为一个字段(可以改变这些默认设置)。一个或多个连续的空格或制表符被看做一个定界符。awk 允许在模式或过程中引用这些字段:0 代表整个输入行,1、

示例1:用于打印文件中每行的第一个字段。

代码语言:javascript复制
$ awk '{ print $1 }' list
John
Alice
Orville
Terry
Eric
Hubert
Amy
Sal

“$1”表示每个输入行上的第一个字段的值。因为没有指定模式,所以打印语句应用于所有行。

示例2:指定一个模式“/MA/”,没有过程。这个默认操作是打印匹配这种模式的每一行。

代码语言:javascript复制
$ awk '/MA/' list
John Daggett, 341 King Road, Plymouth MA
Eric Adams, 20 Post Road, Sudbury MA
Sal Carpenter, 73 6th Street, Boston MA

本例打印了 3 行。awk 更像一种查询语言,从文件中提取有用的信息。可以认为以上模式指定了一种条件,用于选择要包括在报表中的记录,也就是这些记录必须包含字符串“MA”。

示例3:只输出指定记录的第一个字段。

代码语言:javascript复制
$ awk '/MA/ { print $1 }' list
John
Eric
Sal

默认情况下,awk 使用一个或多个空格或制表符作为字段分隔符将输入分隔成字段。

示例4:使用 -F 选项将字段分隔符变为逗号。

代码语言:javascript复制
$ awk -F, '/MA/ { print $1 }' list
John Daggett
Eric Adams
Sal Carpenter

示例5:将每个字段单独打印在一行上,多个指令由分号隔开。

代码语言:javascript复制
$ awk -F, '{ print $1; print $2; print $3 }' list
John Daggett
 341 King Road
 Plymouth MA
Alice Ford
 22 East Broadway
 Richmond VA
Orville Thomas
 11345 Oak Bridge Road
 Tulsa OK
Terry Kalkas
 402 Lans Road
 Beaver Falls PA
Eric Adams
 20 Post Road
 Sudbury MA
Hubert Sims
 328A Brook Road
 Roanoke VA
Amy Wilde
 334 Bayshore Pkwy
 Mountain View CA
Sal Carpenter
 73 6th Street
 Boston MA

本例中使用 awk 重新排列数据,将一行按指定分隔符输出为多行。注意将前导空白看做是第二个和第三个字段的一部分。

2. 出错信息

将遇到程序中的问题时,awk 的每个实现都会给出不同的出错信息:

没有用大括号({})将过程括起来

代码语言:javascript复制
$ awk ' print $1 ' list
awk: cmd. line:1:  print $1 
awk: cmd. line:1:  ^ syntax error

没有用单引号('')将指令括起来

代码语言:javascript复制
$ awk {print $1} list
awk: cmd. line:1: {print
awk: cmd. line:1:       ^ unexpected newline or end of string

没有用斜杠(//)将正则表达式括起来

代码语言:javascript复制
$ awk '/MA' list
awk: cmd. line:1: /MA
awk: cmd. line:1:  ^ unterminated regexp

3. 选项总结

下面汇总了三个 awk 的常用命令行选项:

  • -f:后跟脚本的文件名。
  • -F:指定字段分隔符。
  • -v:后跟 var=value。

三、同时使用 sed 和 awk

示例1:用州的全名替换缩写,并打印州的全名。先创建名为 nameState 的 sed 脚本文件内容如下:

代码语言:javascript复制
s/ CA/, California/
s/ MA/, Massachusetts/
s/ OK/, Oklahoma/
s/ PA/, Pennsylvania/
s/ VA/, Virginia/

然后用管道提取州名:

代码语言:javascript复制
$ sed -f nameState list | awk -F, '{ print $4 }'
 Massachusetts
 Virginia
 Oklahoma
 Pennsylvania
 Massachusetts
 Virginia
 California
 Massachusetts

awk 程序处理由 sed 脚本产生的输出。前面的 sed 脚本用逗号和州的全称代替缩写,它将第三个字段分成两个字段。

示例2:按州的名字排序并列出州名,以及住在那个州的人的名字。下面是 byState 脚本文件的内容:

代码语言:javascript复制
#! /bin/bash
awk -F, '{
    print $4 ", " $0
    }' $* |
sort |
awk -F, '
$1 == LastState {
    print "t" $2
    }
$1 != LastState {
    LastState = $1
    print $1
    print "t" $2
}'

这个脚本有三个部分。程序中调用 awk 以产生 sort 程序的输入,然后再次调用 awk 测试排好序的输入,并确定当前记录中的州的名字,是否与前一个记录中的名字相同。下面来看这个脚本的执行:

代码语言:javascript复制
$ sed -f nameState list | ./byState
 California
     Amy Wilde
 Massachusetts
     Eric Adams
     John Daggett
     Sal Carpenter
 Oklahoma
     Orville Thomas
 Pennsylvania
     Terry Kalkas
 Virginia
     Alice Ford
     Hubert Sims

结果按州名排序,这是使用 awk 从结构化数据中生成报表的典型示例。byState 从 nameState 程序读取输入。默认情况下,sort 程序按字母顺序排列行,从左到右查看字符。为了按州名对记录进行排序,将州名作为排序的关键字插入到记录的开始处。现在 sort 程序可以工作了。注意使用 sort 工具可以避免在 awk 内部编写排序程序。

第二次调用 awk 时执行判断逻辑。脚本查看每条记录的第一个字段以决定它是否与前一条记录相同。如果不同则同时打印州名和人名,如果相同则只打印人名。

这里还包括了给一个变量赋值、测试每个输入行的第一个字段来看它是否等于一个变量字符串、打印制表符来调整输出数据的对齐等功能。注意在使用某个变量之前不必对它赋值,因为 awk 将变量初始化为空字符串。

0 人点赞