匹配引号中的文字(新手)

时间:2009-04-27 07:05:55

标签: regex shell pattern-matching

我在shell编程中完全迷失了,主要是因为我使用的每个站点都提供了不同的工具来进行模式匹配。所以我的问题是用什么工具在管道流中进行简单的模式匹配。

context:我有named.conf文件,我需要在一个简单文件中的所有区域名称进行进一步处理。所以我做〜$ cat named.local | grep区域并在这里完全丢失。我的输出是'zone“domain.tld”{'形式的大约一百个换行符,我需要双引号文本。

感谢您提供了一种方法。

Ĵ

5 个答案:

答案 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

正则表达式为.*"\([^"]*\)".*,匹配:

  1. 任意数量的任何字符:.*
    • 引用:"
    • 以后会开始记住:\(
    • 除引号以外的任何字符:[^"]*
    • 结束小组记住:\)
    • 收盘报价:"
    • 以及任意数量的字符:.*
  2. 调用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中做的话,上面两个中的一个就是我接近它的方式。