2011-05-09 81 views
1

假设我有以下的文本文件(它可以有更多的国家,城市和院校:具有多个字段awk中过滤

begin_state 
New York 
end_state 

begin_cities 
Albany 
Buffalo 
Syracuse 
end_cities 

begin_colleges 
Cornell 
Columbia 
Stony Brook 
end_colleges 

begin_state 
California 
end_ state 

begin_cities 
San Francisco 
Sacramento 
Los Angeles 
end cities 

begin_colleges 
Berkeley 
Stanford 
Caltech 
end_colleges 

我想用awk来过滤所有的城市和下一一列举状态或选择所有的高校和状态下一一列举:例如 - 如果我想在城市中,他们应该是输出如下

**New York** 
Albany 
Buffalo 
Syracuse 
**California** 
San Francisco 
Sacramento 
Los Angeles 

任何建议,欢迎

+0

嗨,如果这是[功课](http://stackoverflow.com/tags/homework/info),这将有助于它作为标记这样。你试过什么了? – neillb 2011-05-10 02:02:00

+0

还要注意你的输入列表至少有两个错误:'end_ state'和'end cities'。 – neillb 2011-05-10 02:02:41

+0

感谢您指出错误。这不是一个家庭作业问题。我没有用awk编码。尝试在excel中没有太大的成功。我在stackoverflow.com上看到了一个单行awk代码,可以很好地处理单个字段过滤问题。 (请参阅http://stackoverflow.com/questions/4705331/filter-text-which-appears-between-two-marks。)在我的问题中,有两对标记:一个始终是“状态”。第二个可以是“城市”或“大学”。所以,我需要打印州名,然后打印城市或大学。其他字段也可以在状态下添加。 – Cyrus 2011-05-10 02:55:12

回答

1

这里有两个SOLUT。 awk中的离子。第一个是天真和重复的,但更容易遵循和学习。后者是减少重复的尝试。

这两种解决方案在处理数据文件中的错误方面都很脆弱。如果你可以自由选择实现语言,我建议你以ruby,perl或python的形式来做到这一点。

保存到一个文件(例如showinfo.sh)并用一个参数:“cities”或“colleges”来调用,以确定模式。你也必须将数据文件重定向到标准输入。

实施例的调用(对于溶液):

./showinfo.sh cities < states.txt 
./showinfo.sh colleges < states.txt 

天真溶液:

#!/bin/bash 
set -e 
set -u 
#mode=cities 
mode=$1 

awk -v mode=$mode ' 
/begin_state/ {st="states"; next} 
/end_state/  {next} 
/begin_cities/ {st="cities"; next} 
/end_cities/  {next} 
/begin_colleges/ {st="coll"; next} 
/end_colleges/ {next} 

{ 
    if (st=="states") { 
    sn=$0; 
    } 
    else 
    if (st=="cities") cities[sn]=cities[sn]"\n"$0 
    else if (st=="coll") colleges[sn]=colleges[sn]"\n"$0; 
} 

END { 
    if (mode=="cities") { 
    for (sn in cities) { print "=="sn"=="cities[sn] } ; 
    } 
    else if (mode=="colleges") { 
    for (sn in colleges) { print "=="sn"=="colleges[sn] } ; 
    } 
    else { print "set mode either cities or colleges" } 
}' 

第二溶液中,用重复移除:

#!/bin/bash 
set -e 
set -u 
mode=$1 
awk -v mode=$mode ' 
/begin_/ {st=$1; next} 
/end_/  {st=""; next} 

{ 
    if (st=="begin_state") { sn=$0 } 
    else { data[st, sn]=data[st, sn]"\n"$0 } 
} 

END { 
    for (combo in data) { 
    split(combo, sep, SUBSEP); 
    type = sep[1]; 
    state_name = sep[2]; 
    if (type == "begin_"mode) { 
     print "==" state_name "==" data[combo]; 
    } 
    } 
}' 
使用

输入文件(如我注意它最近在问题中已经改变):

begin_state 
New York 
end_state 
begin_cities 
Albany 
Buffalo 
Syracuse 
end_cities 
begin_colleges 
Cornell 
Columbia 
Stony Brook 
end_colleges 
begin_state 
California 
end_state 
begin_cities 
San Francisco 
Sacramento 
Los Angeles 
end_cities 
begin_colleges 
Berkeley 
Stanford 
Caltech 
end_colleges 

会话中运行的第一个解决方案时:

$ bash showinfo.sh cities < states.txt 
==New York== 
Albany 
Buffalo 
Syracuse 
==California== 
San Francisco 
Sacramento 
Los Angeles 
+0

如果您使用我描述的示例尝试了您的代码,您是否也可以发布您的unix会话副本?非常感谢! -C。 – Cyrus 2011-05-11 04:31:35

+0

完成。希望有所帮助。 – neillb 2011-05-11 15:47:36

+0

谢谢,它确实帮助我澄清了一些事情。我非常感谢你花时间推出这个漂亮的代码。我现在可以在我的文本文件中尝试一下。希望它可以帮助他人处理类似的过滤问题。 TX。再次。 -C – Cyrus 2011-05-12 02:18:09