Bash: 在字符之间分类一个文件
Bash: Cat a file in between characters
我已经尝试了各种解决方案来找到一种很好的方法来处理以特定单词开头并以特定单词结尾的文件。
假设我有一个名为 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
感谢您花时间和耐心看完这篇文章。感谢您提供的任何帮助。
使用带有模式范围的 sed:
sed '/^Idaho$/,/^South Dakota$/!d' filename
或具有相同模式范围的awk:
awk '/^Idaho$/,/^South Dakota$/' filename
在这两种情况下,^
和 $
分别匹配行的开头和结尾,因此 ^Virginia$
仅在整行为 Virginia
时才匹配(即 West Virginia
不匹配)。
或者,如果您更喜欢固定字符串匹配而不是正则表达式匹配(在这里没有区别,但在其他情况下可能会有所不同):
awk '[=12=] == "Idaho", [=12=] == "South Dakota"' filename
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
此外,它根本不明显,而是一个隐蔽的错误潜入了上面。请注意这个新输入文件中 "c" 和 "d" 之间的附加 "b":
$ 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
但到那时它显然变得有点傻了,你会重写它只使用像我最初建议的标志。
#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
我已经尝试了各种解决方案来找到一种很好的方法来处理以特定单词开头并以特定单词结尾的文件。
假设我有一个名为 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
感谢您花时间和耐心看完这篇文章。感谢您提供的任何帮助。
使用带有模式范围的 sed:
sed '/^Idaho$/,/^South Dakota$/!d' filename
或具有相同模式范围的awk:
awk '/^Idaho$/,/^South Dakota$/' filename
在这两种情况下,^
和 $
分别匹配行的开头和结尾,因此 ^Virginia$
仅在整行为 Virginia
时才匹配(即 West Virginia
不匹配)。
或者,如果您更喜欢固定字符串匹配而不是正则表达式匹配(在这里没有区别,但在其他情况下可能会有所不同):
awk '[=12=] == "Idaho", [=12=] == "South Dakota"' filename
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
此外,它根本不明显,而是一个隐蔽的错误潜入了上面。请注意这个新输入文件中 "c" 和 "d" 之间的附加 "b":
$ 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
但到那时它显然变得有点傻了,你会重写它只使用像我最初建议的标志。
#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