我完全迷失在 shell 编程中,主要是因为我使用的每个站点都提供了不同的工具来进行模式匹配。所以我的问题是使用什么工具在管道流中进行简单的模式匹配。
上下文:我有 named.conf 文件,我需要一个简单文件中的所有区域名称以进行进一步处理。所以我做~$ cat named.local | grep zone 并在这里完全迷失。我的输出是 'zone "domain.tld" {' 形式的约一百个换行符,我需要双引号中的文本。
感谢您展示了一种方法。
Ĵ
我完全迷失在 shell 编程中,主要是因为我使用的每个站点都提供了不同的工具来进行模式匹配。所以我的问题是使用什么工具在管道流中进行简单的模式匹配。
上下文:我有 named.conf 文件,我需要一个简单文件中的所有区域名称以进行进一步处理。所以我做~$ cat named.local | grep zone 并在这里完全迷失。我的输出是 'zone "domain.tld" {' 形式的约一百个换行符,我需要双引号中的文本。
感谢您展示了一种方法。
Ĵ
我认为您正在寻找的是sed
……它是一个流式编辑器,可让您逐行进行替换。
正如您所解释的那样,命令 `cat named.local | grep zone' 给你一个有点像这样的输出:
zone "domain1.tld" {
zone "domain2.tld" {
zone "domain3.tld" {
zone "domain4.tld" {
我猜你希望输出是这样的,因为你说你需要双引号中的文本:
"domain1.tld"
"domain2.tld"
"domain3.tld"
"domain4.tld"
因此,实际上,从每一行中,我们只需要双引号之间的文本(包括双引号本身。)
我不确定您是否熟悉正则表达式,但对于任何编写 shell 脚本的人来说,它们都是非常宝贵的工具。例如,正则表达式/.o.e/
将匹配任何第 2 个字母为小写字母o
且第 4个字母为 的单词的行e
。这将匹配包含诸如“ zone
”、“ tone
”甚至“ I am tone-deaf.
”之类的单词的字符串
诀窍是使用.
(点)字符来表示“任何字母”。还有一些其他特殊字符,例如*
表示“将前一个字符重复 0 次或更多次”。因此,正则表达式 likea*
将匹配“ a
”、“ aaaaaaa
”或空字符串:“”
因此,您可以使用以下方式匹配引号内的字符串:/".*"/
还有一件事你会知道sed
(通过评论,你已经知道了!) - 它允许回溯。一旦你告诉它如何识别一个词,你就可以让它使用那个词作为替换的一部分。例如,假设您想翻转此列表:
Billy "The Kid" Smith
Jimmy "The Fish" Stuart
Chuck "The Man" Norris
进入这个列表:
The Kid
The Fish
The Man
首先,您将在引号内查找字符串。我们已经看到了,它是/".*"/
。
接下来,我们要使用引号内的内容。我们可以使用括号对其进行分组:/"(.*)"/
如果我们想用带下划线的引号替换文本,我们会做一个 replace: s/"(.*)"/_/
,这将给我们留下:
Billy _ Smith
Jimmy _ Stuart
Chuck _ Norris
但我们有回溯!这将让我们回忆一下括号内的内容,使用符号\1
. 因此,如果我们现在这样做:s/"(.*)"/\1/
我们将得到:
Billy The Kid Smith
Jimmy The Fish Stuart
Chuck The Man Norris
因为引号不在括号中,所以它们不是\1
!
为了只将内容留在双引号内,我们需要匹配整行。为此,我们有^
(意思是“行首”)和$
(意思是“行尾”。)
所以现在如果我们使用s/^.*"(.*)".*$/\1/
,我们会得到:
The Kid
The Fish
The Man
为什么?s/^.*"(.*)".*$/\1/
让我们从左到右阅读正则表达式:
s/
- 开始一个替换正则表达式^
- 寻找行首。从那里开始。.*
- 继续,阅读每个字符,直到..."
- ...直到你到达双引号。(
- 开始一组我们可能希望稍后回溯时回忆的角色。.*
- 继续,阅读每个字符,直到...)
- (pssst!关闭组!)"
- ...直到你到达双引号。.*
- 继续,阅读每个字符,直到...$
- 线的尽头!
/
- 使用之后的内容来替换您匹配的内容
\1
- 粘贴匹配的第一组(括号中的内容)的内容。/
- 正则表达式结束用简单的英语:“阅读整行,复制双引号之间的文本。然后用双引号之间的内容替换整行。”
您甚至可以在替换文本周围添加双引号s/^.*"(.*)".*$/"\1"/
,因此我们将得到:
"The Kid"
"The Fish"
"The Man"
这可以用于sed
将行替换为引号中的内容:
sed -e "s/^.*\"\(.*\)\".*$/\"\1\"/"
(这只是外壳转义以处理双引号和斜杠等。)
所以整个命令将类似于:
cat named.local | grep zone | sed -e "s/^.*\"\(.*\)\".*$/\"\1\"/"
好吧,还没有人提到cut
,所以,为了证明有很多方法可以用 shell 做某事:
% grep '^zone' /etc/bind/named.conf | cut -d' ' -f2
"gennic.net"
"generic-nic.net"
"dyn.generic-nic.net"
"langtag.net"
1.
zoul@naima:etc$ cat named.conf | grep zone
zone "." IN {
zone "localhost" IN {
file "localhost.zone";
zone "0.0.127.in-addr.arpa" IN {
2.
zoul@naima:etc$ cat named.conf | grep ^zone
zone "." IN {
zone "localhost" IN {
zone "0.0.127.in-addr.arpa" IN {
3.
zoul@naima:etc$ cat named.conf | grep ^zone | sed 's/.*"\([^"]*\)".*/\1/'
.
localhost
0.0.127.in-addr.arpa
正则表达式是.*"\([^"]*\)".*
,它匹配:
.*
"
\(
[^"]*
\)
"
.*
调用sed
时,语法为's/what_to_match/what_to_replace_it_with/'
. 单引号可以防止您的正则表达式被bash
. 当您使用括号“记住”正则表达式中的某些内容时,您可以将其召回为\1
等\2
。摆弄一会儿。
你应该看看awk。
只要有人指出 sed/awk,我就会指出 grep 是多余的。
sed -ne '/^zone/{s/.*"\([^"]*\)".*/\1/;p}' /etc/bind/named.conf
这为您提供了不带引号的内容(将引号移动到括号内以保留它们)。在 awk 中,使用引号更简单:
awk '/^zone/{print $2}' /etc/bind/named.conf
我尽量避免使用管道(但不是更多)。记住,不要管 cat。这不是必需的。而且,只要 awk 和 sed 复制 grep 的工作,也不要使用管道 grep。至少,不会变成 sed 或 awk。
就个人而言,我可能会使用 perl。但那是因为我可能已经完成了你在 perl 中所做的任何其他事情,使它成为一个小细节(并且能够同时将整个文件和正则表达式对所有内容进行 slurp,忽略 \n's 将是一个奖励我不控制 /etc/bind,例如在共享虚拟主机上)。但是,如果我要在 shell 中执行此操作,则上述两种方法中的一种将是我处理它的方式。