我必须从文件中搜索包含给定单词k次的所有行。我认为我应该使用grep / sed / awk,但我不知道如何。我的想法是使用sed
和grep
逐行检查每一行:
line=1
while [ (sed -n -'($line)p' $name) -n ]; do
if [ (sed -n -'($line)p' $name | grep -w -c $word) -eq "$number" ]; then
sed -n -'($line)p' $name
fi
let line+=1
done
我的第一个问题是我收到以下错误:syntax error near unexpected token 'sed'
。然后我意识到,对于我的测试文件,命令sed -n -'p1' test.txt | grep -w -c "ab"
不会从我的文件返回第一行中“ab”的确切数量(它返回1但有3个幻影)。
我的test.txt
文件:
abc ab cds ab abcd edfs ab
kkmd ab jnabc bad ab
abcdefghijklmnop ab cdab ab ab
abcde bad abc cdef a b
答案 0 :(得分:1)
awk
救援!
$ awk -F'\\<ab\\>' -v count=2 'NF==count+1' file
kkmd ab jnabc bad ab
请注意,\<
和\>
字边界可能是gawk
特定的。
对于变量赋值,我认为最简单的是
$ word=ab; awk -F"\\\<$word\\\>" -v count=2 'NF==count+1' file
kkmd ab jnabc bad ab
答案 1 :(得分:1)
你可以使用grep,但你必须使用它两次。 (你不能使用单个grep,因为ERE无法否定字符串,你只能否定一个括号表达式,它将匹配单个字符。)
以下是使用GNU grep v2.5.1测试的,您可以使用\<
和\>
作为(可能是非便携式)字分隔符:
$ word="ab"
$ < input.txt egrep "(\<$word\>.*){3}" | egrep -v "(\<$word\>.*){4}"
abc ab cds ab abcd edfs ab
abcdefghijklmnop ab cdab ab ab
$ < input.txt egrep "(\<$word\>.*){2}" | egrep -v "(\<$word\>.*){3}"
kkmd ab jnabc bad ab
这里的想法是我们从输入文件行中提取N次出现的单词,然后从该结果中去除任何出现N + 1行的行。出现少于N次的行当然不会被第一次grep匹配。
或者,如果你感觉有点自虐,你也可以用纯粹的狂欢来做这件事:
$ word="ab"; num=3
$ readarray lines < input.txt
$ for this in "${lines[@]}"; do declare -A words=(); x=( $this ); for y in "${x[@]}"; do ((words[$y]++)); done; [ "0${words[$word]}" -eq "$num" ] && echo "$this"; done
abc ab cds ab abcd edfs ab
abcdefghijklmnop ab cdab ab ab
分解以便于阅读(或编写脚本):
#!/usr/bin/env bash
# Salt to taste
word="ab"; num=3
# Pull content into an array. This isn't strictly necessary, but I like
# getting my file IO over with quickly if possible.
readarray lines < input.txt
# Walk through the array (or you could just walk through the input file)
for this in "${lines[@]}"; do
# Initialize this line's counter array
declare -A words=()
# Break up the words into array elements
x=( $this )
# Step though the array, counting each unique word
for y in "${x[@]}"; do
((words[$y]++))
done
# Check the count for "our" word
[ "0${words[$word]}" -eq $num ] && echo "$this"
done
没那么有趣吗? :)
但是这个awk
选项对我来说最有意义。它是一个不依赖于GNU awk的便携式单行程序(因此它可以在OS X,BSD等中运行)
awk -v word="ab" -v num=3 '{for(i=1;i<=NF;i++){a[$i]++}} a[word]==num; {delete a}' input.txt
这可以通过构建一个关联数组来计算每一行上的单词,然后打印该行,如果&#34;有趣&#34;单词是num
指定的内容。它与上面的bash脚本具有相同的基本概念,但是awk让我们做得更好。 :)
答案 2 :(得分:0)
您可以使用grep
执行此操作grep -E "(${word}.*){${number}}" test.txt
这会查找每行${number}
次${word}
次出现。需要使用通配符.*
,因为我们还希望匹配${word}
的匹配并非彼此相邻的匹配项。
这就是我的所作所为:
$ echo 'abc ab cds ab abcd edfs ab
kkmd ab jnabc bad ab
abcdefghijklmnop ab cdab ab ab
abcde bad abc cdef a b' > test.txt
$ word=abc
$ number=2
$ grep -E "(${word}.*){${number}}" test.txt
> abc ab cds ab abcd edfs ab
> abcde bad abc cdef a b
答案 3 :(得分:0)
也许您需要使用sed
。如果您正在寻找字符序列,可以使用这样的代码。但是,它并没有区分单词本身和另一个单词中嵌入的单词(因此它将ab
和abc
视为包含ab
)。
word="ab"
number=2
sed -n -e "/\($word.*\)\{$(($number + 1))\}/d" -e "/\($word.*\)\{$number\}/p" test.txt
-n
)。-e
表达式查找3个(或更多)$word
次出现并删除包含它们的行(并跳到下一行输入)。 $(($number + 1))
为shell arithmetic。-e
表达式查找2次$word
次出现(不会更多)并打印匹配的行。如果你想要自己的话,那么你必须努力工作。您需要使用BSD(Mac OS X)上的-E
选项或GNU -r
sed
触发的扩展正则表达式。
number=2
plus1=$(($number + 1))
word=ab
sed -En -e "/(^|[^[:alnum:]])($word([^[:alnum:]]).*){$plus1}/d" \
-e "/(^|[^[:alnum:]])($word([^[:alnum:]]).*){$number}$word$/d" \
-e "/(^|[^[:alnum:]])($word([^[:alnum:]]|$).*){$number}/p" test.txt
这与之前的版本类似,但它具有更加精细的文字处理。
(^|[^[:alnum:]])
会查找行首或非字母数字字符(如果您不希望数字停止匹配,请将alnum
更改为alpha
-e
查找行首或非字母数字字符,后跟单词和非字母数字以及零个或多个其他字符,N + 1次,并删除此类行(跳过到下一行输入)。-e
查找行首或非字母数字字符,后跟单词和非字母数字以及零个或多个其他字符N次,然后单词再次后跟行尾,并删除这些行。-e
查找行首或非字母数字字符,后跟单词和非字母数字以及零个或多个其他字符N次并打印此类行。给定(扩展)输入文件:
abc NO ab cds ab abcd edfs ab
kkmd YES ab jnabc bad ab
abcd NO efghijklmnop ab cdab ab ab
abcd NO efghijklmnop ab cdab ab ab
abcd NO e bad abc cdef a b
ab YES abcd abcd ab
best YES ab ab candidly
best YES ab ab candidly
ab NO abcd abcd ab ab
hope NO abcd abcd ab ab ab
nope NO abcd abcd ab ab ab
ab YES abcd abcd ab not bad
said YES ab not so bad ab or bad
示例输出:
kkmd YES ab jnabc bad ab
ab YES abcd abcd ab
best YES ab ab candidly
best YES ab ab candidly
ab YES abcd abcd ab not bad
said YES ab not so bad ab or bad
sed
这不是一项微不足道的练习。如果你可以依赖字边界检测会更简单。例如,在Perl中:
number=2
plus1=$(($number + 1))
word=ab
perl -n -e "next if /(\b$word\b.*?){$plus1}/;
print if /(\b$word\b.*?){$number}/" test.txt
这产生与sed
脚本相同的输出,但由于\b
字边界检测(.*?
非贪婪匹配并不重要,因此更加简单对脚本的操作)。