如何在Unix中提取两种模式之间的内容

时间:2015-04-02 13:52:46

标签: shell unix

我有一个包含以下代码的文件test.txt

select * from emp where empid=1;  

select *   
from dep  
where jkdsfj  

select *   
from sal   
where jkdsfj  

我需要在“from”和“where”之间提取内容。

注意:如果新行上有“where”,它仍然必须选择“from”和“where”之间的材料。

输出应该是:

emp  
dep  
sal  

我该怎么做?

4 个答案:

答案 0 :(得分:2)

鉴于需要处理多行,您可以选择sedawk,或者选择一种更复杂的脚本语言,如Perl或Python。

谨慎一点,sed就足够了。我创建了一个文件script.4(创建了scriptscript2,丢失了大部分头发留在我头上的 ** ,然后重新启动{ {1}},script.1script.2,这些都是故意不完整的),如下所示:

script.3

我创建了一个测试文件/from.*where/ { s/.*from *//; s/ *where.*//; p; n; } /from/,/where/ { s/.*from *//; s/ *where.*//; /^ *$/d; p; } ,如下所示:

data

并像这样运行命令,以显示输出:

select * from emp where empid=1;  

select *   
from dep  
where jkdsfj  

select *   
from sal   
where jkdsfj  

select elephants
from abject poverty
join flying tigers
where abelone = shellfish;

select mouse
from toolset
join animals where tail = cord
and buttons = legs

脚本简单'。对于同时包含$ sed -n -f script.4 data emp dep sal abject poverty join flying tigers toolset join animals $ from的行,请删除where之后的所有内容(以及后面的任何空格),删除from之前的所有内容(以及之前的任何空格) it),打印剩下的内容,然后转到下一行输入。

否则,在包含where的行和包含from的行之间, 删除where之后的所有内容(以及后面的任何空格),删除from以后的所有内容(加上前面的任何空格),如果该行为空,则将其删除;否则打印出来。请注意,向第二行添加where命令会导致脚本行为异常(我需要花时间计算原因),但删除操作可以添加到第一个命令行而不会造成任何伤害(如果一行包含n,没有打印任何内容。

请注意,有许多SELECT语句会被此代码错误处理。

例如:

from where

除了大写关键字之外,子查询中的WHERE将是FROM和WHERE之间的匹配停止的位置。


**如果您对脱发的原因感到好奇,请查看Why does an n instead of a b or d or nothing change the behaviour of sed in this script?

答案 1 :(得分:0)

Jonathan Leffler's answer中提到的警告适用:不能使用嵌套 SQL语句。

这是一个结合trsed的实用解决方案:

使用 GNU Sed:

tr -s ' ' '\n' < test.txt | sed -n '/^from$/I,/^where$/I { s///; t; p; }'

使用 BSD Sed(也用于OSX;这是一个符合POSIX标准的解决方案,与GNU Sed一起使用) - 请注意使用I不幸的是,在 BSD Sed中不支持不区分大小写的匹配,因此以下内容仅匹配全小写fromwhere

tr -s ' ' '\n' < test.txt | sed -n -e '/^from$/,/^where$/ { s///; t' -e 'p; }'
  • tr -s ' ' '\n'有效地将输入拆分为单独的令牌,每个令牌都在一个单独的行上。
  • sed命令然后提取表名:
    • 请注意,GNU和BSD命令之间的唯一区别是BSD Sed需要t之后的换行符,在这种情况下,通过将其余脚本作为单独的{{1}提供隐式提供。选项:
    • -e匹配行的范围包括 /^from$/,/^where$/from行。
    • where是一种跳过s///; tfrom行的技巧,实际上只打印(where之间的 他们:
      • p是一个虚拟替换:
        • 未指定s///内的正则表达式意味着重复使用与当前行匹配的相同正则表达式。
        • 替换的结果是无关紧要的 - 唯一的一点是执行任何替换,只会发生在范围的端点
      • //然后分支 - 在没有目标标签名称的情况下 - 到脚本的 end ,如果发生了替换 - 这只是端点范围,有效地跳过它们。
      • t - 即打印当前行 - 仅针对 pfrom行之间的行执行。

警告:如果在wherefrom之间存在多个以空格分隔的标记,则它们将在单独的行中输出。< / p>

答案 2 :(得分:0)

Jonathan Leffler's answer中提到的警告适用:不适用于嵌套 SQL语句。

如果您使用的是 GNU grep(如Linux上所示),请尝试以下操作:

tr -s '\n' ' ' < test.txt | grep -Pio '(?<= from ).*?(?= where )'
  • tr -s '\n' ' '替换每行带有空格的换行符,从而生成单个行。
    • (这样做的副作用是,如果from / where对内的令牌跨越多行,则会将它们输出为单行,以空格分隔的列表。)
  • grep命令:
    • -P激活对PCRE(Perl兼容的正则表达式)的支持,它提供了先行和后置断言等高级功能。
    • -o导致Grep仅输出每行的匹配部分,而i执行不区分大小写的匹配。
    • (?<= from )使用后备断言来匹配from而不将其包含在匹配中
    • (?<= where )使用预见断言匹配where,而不将其包含在匹配中
    • .*? 非贪婪地匹配任何字符序列;非贪婪修饰符?是阻止.*通过输入行上 last 出现的where进行匹配所必需的。

BSD grep(也在OSX上使用)不支持-P,因此需要额外的提取步骤来删除from和来自匹配项的where关键字,使用awk

tr -s '\n' ' ' < test.txt | grep -Eio ' from .*? where ' | 
  awk -F ' from | where ' '{ print $2 }'

答案 3 :(得分:-1)

不漂亮,但工作(在同一条线上):

grep "from.*where" test.txt | awk '{ print $2 }'

对于通用解决方案,我使用python,因为grep不能用于多行。