10

在扫描文本文件和查找两种模式之间的所有单词时需要帮助。比如说如果我们有一个 .sql 文件,需要扫描并找到 from' 和 'where' 之间的所有单词。Grep 一次只能扫描 1 行。对于这个要求,最好使用的 unix 脚本是什么?sed、awk有这些功能吗?非常感谢您指出任何示例。

4

5 回答 5

23

Sed 有这个:

sed -n -e '/from/,/where/ p' file.sql

打印带有 afrom的行和带有 a 的行之间的所有行where

对于可以包含同时具有 from 和 where 的行的内容:

#!/bin/sed -nf

/from.*where/ {
    s/.*\(from.*where\).*/\1/p
    d
}
/from/ {
    : next
    N
    /where/ {
        s/^[^\n]*\(from.*where\)[^\n]*/\1/p
        d
    }
    $! b next
}

这(编写为 sed 脚本)稍微复杂一些,我将尝试解释细节。

第一行在包含 afrom和 a的行上执行where。如果一行与该模式匹配,则执行两个命令。我们使用s替换命令仅提取 from 和 where 之间的部分(包括 from 和 where)。该p命令中的后缀打印该行。delete 命令清除模式空间(工作缓冲区),加载下一行并重新启动脚本。

当找到包含的行时,第二个命令开始执行一系列命令(由大括号分组)from。基本上,这些命令形成一个循环,它将不断将输入中的行追加到模式空间中,直到找到带有 a 的行where或直到我们到达最后一行。

:命令”创建一个标签,脚本中的一个标记,允许我们在需要时“跳”回来。该N命令从输入中读取一行,并将其附加到模式空间(用换行符分隔行)。

where找到 a 时,我们可以打印出模式空间的内容,但首先我们必须用替换命令清理它。它与之前使用的类似,但我们现在用 替换前导和尾随.*[^\n]*这告诉 sed 仅匹配非换行符,有效地匹配第一行中的 a from 和最后一行中的 a where。然后该d命令清除模式空间并在下一行重新启动脚本。

b命令将跳转到一个标签,在我们的例子中是 label next。但是,$!地址说它不应该在最后一行执行,让我们离开循环。以这种方式离开循环时,我们没有找到相应的where,因此您可能不想打印它。

但是请注意,这有一些缺点。以下情况将不会按预期处理:

from ... where ... from

from ... from
where

from
where ... where

from
from
where
where

处理这些情况需要更多代码。

希望这会有所帮助=)

于 2012-10-16T15:42:02.920 回答
2

使用 GNU awk,您可以将 RS 设置为 RE:

gawk -v RS='[[:space:]]+' '
   /where/ { found=0 }
   found   {  print  }
   /from/  { found=1 }
' file

以上假设您不希望打印“from”和“where”,如有必要,请移动线条。

如果有帮助,以下成语描述了如何选择给定特定模式的记录范围进行匹配:

a) 从某个模式打印所有记录:

awk '/pattern/{f=1}f' file

b) 以某种模式打印所有记录:

awk 'f;/pattern/{f=1}' file

c) 在某种模式之后打印第 N 条记录:

awk 'c&&!--c;/pattern/{c=N}' file

d) 在某种模式之后打印除第 N 条记录之外的每条记录:

awk 'c&&!--c{next}/pattern/{c=N}1' file

e) 在某种模式之后打印 N 条记录:

awk 'c&&c--;/pattern/{c=N}' file

f) 在某种模式之后打印除 N 条记录之外的每条记录:

awk 'c&&c--{next}/pattern/{c=N}1' file

g) 从某个模式打印 N 条记录:

awk '/pattern/{c=N}c&&c--' file

我将变量名称从“found”的“f”更改为“count”的“c”,因为这更能表达变量的实际含义。

于 2012-10-16T16:09:46.330 回答
1

您可以使用ed它,它允许正则表达式范围的正负偏移。如果输入是:

seq 10 | tee > infile
1
2
3
4
5
6
7
8
9
10

在命令中输入管道ed

<<< /3/,/6/p | ed -s infile

即打印包含3和的行之间的所有内容6

结果:

3
4
5
6

要在每一端多取一行:

<<< /3/-1,/5/+1p | ed -s infile

结果:

2
3
4
5
6
7

或者反过来:

<<< /3/+1,/6/-1p | ed -s infile

结果:

4
5
于 2012-10-17T00:34:27.230 回答
1

我只用 grep 就可以做到这一点:

#> grep -A#### "start pattern" file | grep -B#### "end pattern"

问题是我必须找到正确数量的行来包含在 A 和 B 选项中,它们是相同的。希望这可以帮助

于 2014-02-26T20:25:37.983 回答
0

为了只返回两个给定字符串中的一个字符串,按照awk(不发疯)我只是运行这个非常扁平的脚本,拖曳冗长:

.\gnucoreutils\bin\awk "{startstring = \"RETURN STUFF AFTER ME \"; endstring = \"RETURN STUFF BEFORE ME\"; endofstartstring = index($0,startstring)+length(startstring); print substr($0,endofstartstring,index($0,endstring)-endofstartstring)}" /dev/stdin

请注意,我使用的是cmd.exe(Windows 的命令解释器)和gnuwin32 awk,所以请注意“双引号”和 ^\转义字符^\:

GNU Awk 3.1.6
Copyright (C) 1989, 1991-2007 Free Software Foundation.

请指出缺陷。

例子:

echo "hello. RETURN STUFF AFTER ME i get returned RETURN STUFF BEFORE ME my face is melting" | .\gnucoreutils\bin\awk "{startstring = \"RETURN STUFF AFTER ME \"; endstring = \" RETURN STUFF BEFORE ME\"; endofstartstring = index($0,startstring)+length(startstring); print substr($0,endofstartstring,index($0,endstring)-endofstartstring)}" /dev/stdin
i get returned
于 2013-01-24T17:54:31.280 回答