我在shell编程中完全迷失了,主要是因为我使用的每个站点都提供了不同的工具来进行模式匹配。所以我的问题是用什么工具在管道流中进行简单的模式匹配。
context:我有named.conf文件,我需要在一个简单文件中的所有区域名称进行进一步处理。所以我做〜$ cat named.local | grep区域并在这里完全丢失。我的输出是'zone“domain.tld”{'形式的大约一百个换行符,我需要双引号文本。
感谢您提供了一种方法。
Ĵ
答案 0 :(得分:23)
我认为您正在寻找的是sed
...这是 s tream ed itor,它可让您在线路上进行替换 - 按行格式。
正如您所解释的那样,命令`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"
所以,实际上,从每一行我们只想要双引号之间的文本(包括双引号本身。)
我不确定你是否熟悉Regular Expressions,但对于任何编写shell脚本的人来说,它们都是非常宝贵的工具。例如,正则表达式/.o.e/
将匹配任何一行,其中第二个字母的单词是小写o
,第四个是e
。这会匹配包含“zone
”,“tone
”或甚至“I am tone-deaf.
”等字词的字符串
诀窍是使用.
(点)字符表示“任何字母”。还有一些其他特殊字符,例如*
,表示“重复前一个字符0次或更多次”。因此,a*
之类的正则表达式将匹配“a
”,“aaaaaaa
”或空字符串:“”
因此,您可以使用以下内容匹配引号内的字符串:/".*"/
关于sed
,您会知道另一件事(并且通过评论,您已经做过了!) - 它允许回溯。一旦你告诉它如何识别一个单词,你可以让它作为替换的一部分使用该单词。例如,假设您要转换此列表:
Billy "The Kid" Smith
Jimmy "The Fish" Stuart
Chuck "The Man" Norris
进入此列表:
The Kid
The Fish
The Man
首先,你要在引号内查找字符串。我们已经看到了,它是/".*"/
。
接下来,我们想要使用引号内的内容。我们可以使用parens 分组:/"(.*)"/
如果我们想用引号替换带有下划线的文字,我们会执行替换:s/"(.*)"/_/
,这样就可以了:
Billy _ Smith
Jimmy _ Stuart
Chuck _ Norris
但我们有回溯!那将让我们使用符号\1
来回想一下parens中的内容。所以如果我们现在这样做:s/"(.*)"/\1/
我们会得到:
Billy The Kid Smith
Jimmy The Fish Stuart
Chuck The Man Norris
因为引号不在parens中,所以它们不属于\1
的内容!
要将内容留在双引号中,我们需要匹配整行。要做到这一点,我们有^
(表示“行首”)和$
(表示“行尾”。)
现在,如果我们使用s/^.*"(.*)".*$/\1/
,我们将获得:
The Kid
The Fish
The Man
为什么呢?让我们从左到右阅读正则表达式s/^.*"(.*)".*$/\1/
:
s/
- 开始替换正则表达式^
- 寻找行的开头。从那里开始。.*
- 继续前进,阅读每一个角色,直到...... "
- ...直到你达到双引号。(
- 在回溯时,我们可能会想要回忆一个字符组。.*
- 继续前进,阅读每一个角色,直到...... )
- (pssst!关闭小组!)"
- ...直到你达到双引号。.*
- 继续前进,阅读每一个角色,直到...... $
- 行尾!
/
- 使用此后的内容替换您匹配的内容
\1
- 粘贴匹配的第一组内容(parens中的内容)。/
- 正则表达式结束用简单的英语:“阅读整行,将双引号之间的文本复制一边。然后用双qoutes之间的内容替换整行。”
您甚至可以在替换文字s/^.*"(.*)".*$/"\1"/
周围添加双引号,这样我们就可以获得:
"The Kid"
"The Fish"
"The Man"
sed
可以使用它来用引号内的内容替换该行:
sed -e "s/^.*\"\(.*\)\".*$/\"\1\"/"
(这只是shell转义处理双引号和斜线和东西。)
所以整个命令就像是:
cat named.local | grep zone | sed -e "s/^.*\"\(.*\)\".*$/\"\1\"/"
答案 1 :(得分:2)
嗯,没有人提到cut
,所以,要证明有很多方法可以对shell做点什么:
% grep '^zone' /etc/bind/named.conf | cut -d' ' -f2
"gennic.net"
"generic-nic.net"
"dyn.generic-nic.net"
"langtag.net"
答案 2 :(得分:1)
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
展开。当您使用parens“记住”正则表达式中的某些内容时,您可以将其记为\1
,\2
等。暂时解决它。
答案 3 :(得分:0)
您应该查看awk。
答案 4 :(得分:0)
只要有人指出sed / awk,我就会指出grep是多余的。
sed -ne '/^zone/{s/.*"\([^"]*\)".*/\1/;p}' /etc/bind/named.conf
这样可以在没有引号的情况下为您提供所需的内容(在括号内移动引号以保留它们)。在awk中,引号更简单:
awk '/^zone/{print $2}' /etc/bind/named.conf
我尽量避免使用管道(但不是更多)。请记住,Don't pipe cat。这不是必需的。而且,就像awk和sed重复grep的工作一样,也不要管grep。至少,不要进入sed或awk。
就个人而言,我可能已经使用过perl。但那是因为我可能已经完成了你在perl中所做的其余工作,使其成为一个小细节(并且能够将整个文件和/正则表达式同时对抗所有内容,忽略\ n \ n \ n \ n \ n对于我不控制/ etc / bind,例如在共享的webhost上)。但是,如果我是在shell中做的话,上面两个中的一个就是我接近它的方式。