我需要计算文本文件 ( ) 中包含单词the
和 的行数,但是an
poem.txt
不是同时包含两者的。
我尝试过使用
grep -c the poem.txt | grep -c an poem.txt
the
但是当和 的总数an
为 9 行时,这给了我 6 的错误答案。
我确实想计算包含单词的行数,而不是单词本身。只有实际单词才算数,so the
but notthere
和 and an
but not Pan
。
示例文件:poem.txt
Where is the misty shark?
Where is she?
The small reef roughly fights the mast.
Where is the small gull?
Where is he?
The gull grows like a clear pirate.
Clouds fall like old mainlands.
She will Rise calmly like a dead pirate.
Eat an orange.
Warm, sunny sharks quietly pull a cold, old breeze.
All ships command rough, rainy sails.
Elvis Aaron Presley also known simply as the Elvis
He is also referred to as the King
The best-selling solo music artist of all time
He was the most commercially successful artist in many genres
He has many awards including a Grammy lifetime achievement
Elvis in the 1970s has numerous jumpsuits including an eagle one.
进一步澄清:这首诗中有多少行包含 the
或但你不应该计算同时包含和an
的行。the
an
the car is red - this counted
an apple is in the corner - not counted
hello i am big - not counted
where is an apple - counted
所以这里的输出应该是2。
编辑:我不担心区分大小写。
最终编辑:感谢您的所有帮助。我已经成功解决了这个问题。我使用了答案之一并对其进行了一些更改。我 曾经如何将第二个 grep 中cat poem.txt | grep -Evi -e '\<an .* the\>' -e '\<the .* an\>' | grep -Eci -e '\<(an|the)\>
的 更改为 a以获取一些附加信息。再次感谢您的所有帮助! :)-c
-n
答案1
perl -nE 'END {say $c+0} ++$c if /\bthe\b/i xor /\ban\b/i' file
gawk 'END {print c+0} /\<the\>/ != /\<an\>/ {++c}' IGNORECASE=1 file
比较每个表达式的匹配结果可以得到您想要的结果。
例如,匹配的结果\<the\>
可能是 0 或 1。如果另一个匹配的结果相同,则两个正则表达式要么找到,要么找不到,并且该行不应被计数。如果它们不同,则意味着找到了一个匹配项而没有找到另一个匹配项,因此计数器会递增。
gawk 有一个内置xor()
函数:
gawk 'END {print c+0} xor(/\<the\>/,/\<an\>/) {++c}' IGNORECASE=1 file
答案2
使用 grep:
cat poem.txt \
| grep -Evi -e '\<an\>.*\<the\>' -e '\<the\>.*\<an\>' \
| grep -Eci -e '\<(an|the)\>'
这算的是匹配线。您可以找到一种替代语法来计算总数火柴在下面。
分解:
首先 grep 命令过滤掉所有包含“an”和“the”的行。第二个 grep 命令对包含“an”或“the”的行进行计数。
c
如果您从第二个 grep 中删除-Eci
,您将看到所有匹配项都突出显示。
细节:
该
-E
选项为 grep 启用扩展表达式语法 (ERE)。该
-i
选项告诉 grep 匹配时不区分大小写该
-v
选项告诉 grep 反转结果(即匹配行不是包含模式)该
-c
选项告诉 grep 输出匹配行的数量而不是行本身模式:
--> 这样我们就可以确保不匹配单词含有“the”或“an”(如“pan”)
grep -Evi -e '\<an\>.*\<the\>'
因此匹配所有行不是包含“一个...的”同样,
grep -Evi -e '\<the\>.*\<an\>'
匹配所有行不是包含“the ... an”grep -Evi -e '\<an\>.*\<the\>' -e '\<the.*an\>'
是 3. 和 4. 的组合。grep -Eci -e '\<(an|the)\>'
匹配包含“an”或“the”(由空格或行首/行尾包围)的所有行并打印匹配行数
编辑1:按照@glenn-jackman的建议,使用\<
and\>
代替( |^)
and( |$)
编辑2:为了计算匹配数而不是匹配行数,请使用以下表达式:
cat poem.txt \
| grep -Evi -e '\<an\>.*\<the\>' -e '\<the\>.*\<an\>' \
| grep -Eio -e '\<(an|the)\>' \
| wc -l
这使用了-o
grep 选项,它将每个匹配打印在单独的行中(没有其他内容),然后wc -l
对行进行计数。
答案3
下面的 GNUawk
程序应该可以解决这个问题:
awk '(/(^|\W)[Tt]he(\W|$)/ && !/(^|\W)[Aa]n(\W|$)/) || (/(^|\W)[Aa]n(\W|$)/ && !/(^|\W)[Tt]he(\W|$)/) {c++} END{print c}' poem.txt
这将增加计数器c
,如果
- 该行匹配
(^|\W)[Tt]he(\W|$)
(首字母不区分大小写the
,前面是非单词成分 (\W
) 或行首 (^
),后面是非单词成分 (\W
) 或行尾 ($
)),但不匹配(^|\W)[Aa]n(\W|$)
(孤立的第一个- 不区分字母大小写an
) - 或 - - 该行匹配
(^|\W)[Aa]n(\W|$)
但不匹配(^|\W)[Tt]he(\W|$)
最后打印 的值c
。
可以使用\<
and\>
作为“词开头”和“词结尾”来将其表述得稍微短一些:
awk '(/\<[Tt]he\>/ && !/\<[Aa]n\>/) || (/\<[Aa]n\>/ && !/\<[Tt]he\>/) {c++} END{print c}' poem.txt
甚至更短的是:
awk '/\<[Tt]he\>/ != /\<[Aa]n\>/ {c++} END{print c}' poem.txt
an
因为只有当 和 中的任何一个出现在一条线上,但不是两者(或没有)出现在一条线上时,不等式才为真the
。
这种方法需要 GNU,awk
因为\W
and \<
/\>
结构是扩展正则表达式语法的 GNU 扩展(但\<
/\>
也可以被理解为BSD 正则表达式)。
请注意,您在自己尝试的解决方案中显示的管道构造将不起作用,因为使用grep
文件作为输入参数进行调用取代了从 stdin 读取,因此管道的第一部分会在不被注意的情况下消失,输出完全是由于最后一部分(查找 的出现an
,甚至是嵌入在其他单词中的那些)。
答案4
您可以使用 GNU grep 和 PCRE 零长度断言来做到这一点:
grep -iP '(?=.*\bthe\b)(?!.*\ban\b)|(?=.*\ban\b)(?!.*\bthe\b)' poem.txt
Where is the misty shark?
...
Eat an orange.
...
grep -ciP '(?=.*\bthe\b)(?!.*\ban\b)|(?=.*\ban\b)(?!.*\bthe\b)' poem.txt
9
同样的功能在 perl 中可用(它的起源地),并且 perl 可能存在于 GNU grep 不存在的机器上。