Bash:在角色之间捕捉一个文件

时间:2015-04-21 19:46:06

标签: bash awk sed grep cat

我尝试了各种解决方案来找到一个好方法来浏览一个以特定单词开头的文件,并以特定单词结尾。

假设我有一个名为states.txt的文件,其中包含:

Alabama
Alaska
Arizona
Arkansas
California
Colorado
Connecticut
Delaware
Florida
Georgia
Hawaii
Idaho
Illinois 
Indiana
Iowa
Kansas
Kentucky
Louisiana
Maine
Maryland
Massachusetts
Michigan
Minnesota
Mississippi
Missouri
Montana 
Nebraska
Nevada
New Hampshire
New Jersey
New Mexico
New York
North Carolina
North Dakota
Ohio
Oklahoma
Oregon
Pennsylvania 
Rhode Island
South Carolina
South Dakota
Tennessee
Texas
Utah
Vermont
Virginia
Washington
West Virginia
Wisconsin
Wyoming

我想要cat states.txt并获得以Idaho开头并以South Dakota结尾的以下状态。

我也想忽略这样一个事实:状态是按字母顺序排列的(我要的实际文件内容不是按照这样的顺序)。

结果如下:

Idaho
Illinois 
Indiana
Iowa
Kansas
Kentucky
Louisiana
Maine
Maryland
Massachusetts
Michigan
Minnesota
Mississippi
Missouri
Montana 
Nebraska
Nevada
New Hampshire
New Jersey
New Mexico
New York
North Carolina
North Dakota
Ohio
Oklahoma
Oregon
Pennsylvania 
Rhode Island
South Carolina
South Dakota

感谢您抽出宝贵的时间和耐心。我感谢您提供的任何帮助。

3 个答案:

答案 0 :(得分:7)

使用带有模式范围的sed:

sed '/^Idaho$/,/^South Dakota$/!d' filename

或者具有相同模式范围的awk:

awk '/^Idaho$/,/^South Dakota$/' filename

在这两种情况下,^$分别匹配行的开头和结尾,因此^Virginia$仅在整行为Virginia时匹配(即,West Virginia不匹配。)

或者,如果您更喜欢固定字符串匹配而不是正则表达式匹配(它在这里没有区别,但可能在其他情况下):

awk '$0 == "Idaho", $0 == "South Dakota"' filename

答案 1 :(得分:7)

awk '/Idaho/{f=1} f; /South Dakota/{f=0}' file

有关更多awk范围的习语,请参阅Explain awk command

不要养成使用/start/,/end/的习惯,因为它使得琐碎的事情变得非常简单,但即使最轻微的要求发生变化(例如不打印边界线),也需要完全重写或复制条件。

例如,给定此输入文件:

$ cat file
a
b
c
d
e

打印b和d之间的直线,然后排除其中一条或两条边界线:

$ awk '/b/{f=1} f; /d/{f=0}' file
b
c
d

$ awk 'f; /b/{f=1} /d/{f=0}' file
c
d

$ awk '/b/{f=1} /d/{f=0} f;' file
b
c

$ awk '/d/{f=0} f; /b/{f=1}' file
c

如果你的出发点是awk '/b/,/d/' file,请注意并注意其他语言结构和所需的重复条件:

$ awk '/b/,/d/' file
b
c
d

$ awk '/b/,/d/{if (!/b/) print}' file
c
d

$ awk '/b/,/d/{if (!/d/) print}' file
b
c

$ awk '/b/,/d/{if (!(/b/||/d/)) print}' file
c

此外,它并不明显,只是一个阴险的错误悄悄进入上面。请注意附加的" b"现在介于" c"和" d"在这个新的输入文件中:

$ cat file
a
b
c
b
d
e

并再次尝试从输出中排除第一个边界线:

$ awk 'f; /b/{f=1} /d/{f=0}' file
c
b
d
-> SUCCESS

$ awk '/b/,/d/{if (!/b/) print}' file
c
d
-> FAIL

你实际上需要写这样的东西来继续使用范围并排除第一个边界线

$ awk '/b/,/d/{if (c++) print; if (/d/) c=0}' file
c
b
d

但到那时它显然变得有点傻了,你重写它只是使用像我原来的建议一样的旗帜。

答案 2 :(得分:0)

#all bash
__IFS=$IFS
IFS=' '
list=$(cat file.txt)
start="Idaho"
stop="South Dakota"
fst=${list#*$start}
snd=${fst%$stop*}
result="$start$snd$stop"
echo $result
IFS=$__IFS

请参阅http://tldp.org/LDP/abs/html/string-manipulation.html