如何从命令行操作基于字段的数据?例如
- 如何只打印第 N 个字段为 的行
foo
? - 如何只打印第 N 个字段不是 的行
foo
? - 如何只打印第 N 个字段匹配的行
foo
? - 如何将字段 N 更改为
foo
?
是否有一个标准方法或工具集可以帮助在 *nix 系统上操作基于现场的数据?
答案1
处理字段时可以使用两种基本方法:i)使用理解字段的工具; ii) 使用正则表达式。在两者中,前者通常更强大且更简单。
*nix 上的许多常用工具要么明确设计用于处理字段,要么具有巧妙的技巧来促进它。
1. 使用理解字段的工具
1.1 awk
这里的经典工具是awk
.它将自动将每个输入行拆分为字段(默认情况下字段分隔符为空格,但可以使用标志进行更改-F
),然后这些字段可用于awk
脚本,如下所示$n
n
是字段编号。第一个字段是$1
,第二个字段$2
等等。
打印第三个字段为 的行
foo
。awk '$3=="foo"' file
将分隔符更改为
:
awk -F":" '$3=="foo"' file
默认操作
awk
是打印。因此上面的命令将打印第三个字段为 的所有行foo
。使用时-F
,您可以设置任意字段分隔符,甚至可以使用正则表达式。如何只打印第三个字段不是的行
foo
?awk '$3!="foo"' file
如何只打印第三个字段匹配的行
foo
?如果您只是查找与模式匹配的字段(例如
foo
matchesfoobar
),请使用~
而不是==
:awk '$3~/foo/' file
如何仅打印第三个字段不匹配的行
foo
?awk '$3!~/foo/' file
如何将第三个字段更改为
foo
?awk '$3="foo"' file
1.2 珀尔
另一种选择是perl
单行。与 awk 一样,Perl 是一种功能齐全的脚本语言,但也可以作为命令行程序运行,以脚本作为输入。它的行为是通过命令行开关修改的,其中与这个问题最相关的是:
-e
perl
:应该运行的脚本;-n
:逐行读取输入文件;-p
:应用 ; 给出的脚本后打印每个输入行-e
;-l
print
:从每个输入行中删除尾随换行符,并为每个调用添加一个换行符;-a
:awk-模式,将每个输入行拆分到数组中@F
;-F
: 的字段分隔符-a
。
一个重要的区别awk
是perl
's -a
switch 将文件分割成一个数组。在 Perl 中,数组从 0 开始,而不是 1。这意味着第二个字段实际上是$F[1]
而不是$F[2]
。考虑到所有这些,perl
上面的等价物是:
打印第三个字段为 的行
foo
。perl -ane 'print if $F[2] eq "foo"' file
将分隔符更改为
:
perl -F":" -ane 'print if $F[2] eq "foo"' file
与 不同的是
awk
,perl
不能使用正则表达式作为字段分隔符。它们必须是特定的字符或字符串。如何只打印第三个字段不是的行
foo
?perl -ane 'print unless $F[2] eq "foo"' file
如何只打印第三个字段匹配的行
foo
?perl -ane 'print if $F[2]=~/foo/' file
如何仅打印第三个字段不匹配的行
foo
?perl -lane 'print unless $F[2]=~/foo/' file
如何将第三个字段更改为
foo
?这在 Perl 中有点麻烦。通常的方法是更改数组中的值
@F
,然后打印数组。对于简单的空格分隔文件,这很容易:perl -lane '$F[2]="foo"; print "@F"' file
使用不同的分隔符,您将需要
join
数组。否则,它将以空格分隔打印:perl -F: -lane '$F[2]="foo"; print join ":",@F' file
2. 使用正则表达式
这里的想法是使用正则表达式(简称“regex”)来定义目标字符串在行中的位置。例如,在字段由 分隔的文件中:
,我们可以通过匹配第一个字段(第一个字段)之前的所有内容:
,然后查找第二个字段来找到第二个字段:
^[^:]*:[^:]*:
这个正则表达式的意思是:
^
:行的开头;[^]
:否定的字符类。[^:]
意思是“除了:
”之外的任何事情;*
: 0 个或多个先前模式;:
:字面意思:
;
总的来说,这意味着第一个[^:]*
是第一个字段,第二个是第二个字段。显然,如果您正在寻找第 14 个字段,这不是很实用,但它对于更简单的事情很有用。那么,我们如何实现它来操纵我们的数据呢?有多种工具可以做到这一点;在这些示例中,我将使用,sed
但您可以使用awk
,perl
或做非常类似的事情python
。
如何仅打印第二个字段为 的行
foo
?sed -n '/^[^:]*:foo:/p' file
抑制
-n
正常输出,/regex/p
意思是“打印正则表达式匹配的任何行。如何只打印第二个字段不是的行
foo
?sed '/^[^:]*:foo:/d' file
与上述逻辑相反。这里,
/regex/d
意思是“删除正则表达式匹配的任何行。如何仅打印第二个字段匹配的行
foo
?sed -n '/^[^:]*:[^:]*foo/p' file
如何仅打印第二个字段不匹配的行
foo
?sed '/^[^:]*:[^:]*foo/d' file
如何将第二个字段更改为
foo
?sed 's/\([^:]*:\)[^:]*/\1foo/' file
或者,由于
sed
替换可以通过使用简单的数字标志重复来直接解决模式的出现:sed 's/[^:]*/foo/2' file