我想要一个 unix 命令来查找单词的第一次和最后一次出现之间的行
例如:
假设我们有 1000 行。第十行包含单词“stackoverflow”,第三十五行还包含单词“stackoverflow”。
我想打印 10 到 35 之间的行并将其写入一个新文件。
您可以分两步完成。基本思想是:
1)获取第一个和最后一个匹配的行号。
2)打印这些范围之间的行范围。
$ read first last <<< $(grep -n stackoverflow your_file | awk -F: 'NR==1 {printf "%d ", $1}; END{print $1}')
$ awk -v f=$first -v l=$last 'NR>=f && NR<=l' your_file
read first last
读取两个值并将它们存储在$first
和中$last
。grep -n stackoverflow your_file
greps 并显示如下输出:number_of_line:output
awk -F: 'NR==1 {printf "%d ", $1}; END{print $1}')
打印stackoverflow
文件中第一个和最后一个匹配的行数。和
awk -v f=$first -v l=$last 'NR>=f && NR<=l' your_file
打印从$first
行号到$last
行号的所有行。$ cat a
here we
have some text
stackoverflow
and other things
bla
bla
bla bla
stackoverflow
and whatever else
stackoverflow
to make more fun
blablabla
$ read first last <<< $(grep -n stackoverflow a | awk -F: 'NR==1 {printf "%d ", $1}; END{print $1}')
$ awk -v f=$first -v l=$last 'NR>=f && NR<=l' a
stackoverflow
and other things
bla
bla
bla bla
stackoverflow
and whatever else
stackoverflow
按步骤:
$ grep -n stackoverflow a
3:stackoverflow
9:stackoverflow
11:stackoverflow
$ grep -n stackoverflow a | awk -F: 'NR==1 {printf "%d ", $1}; END{print $1}'
3 11
$ read first last <<< $(grep -n stackoverflow a | awk -F: 'NR==1 {printf "%d ", $1}; END{print $1}')
$ echo "first=$first, last=$last"
first=3, last=11
如果您知道可以有多少行的上限(例如,一百万行),那么您可以使用这个简单的滥用脚本:
(grep -A 100000 stackoverflow | grep -B 1000000 stackoverflow) < file
您也可以附加| tail -n +2 | head -n -1
以去除边框线:
(grep -A 100000 stackoverflow | grep -B 1000000 stackoverflow
| tail -n +2 | head -n -1) < file
对于输出是否应该包含第一行和最后一个匹配行的问题,我不能 100% 确定,所以我假设它是。但是如果我们想要独占,这可以很容易地改变。
这个纯 bash 解决方案一步完成 - 即文件(或管道)只读取一次:
#!/bin/bash
function midgrep {
while read ln; do
[ "$saveline" ] && linea[$((i++))]=$ln
if [[ $ln =~ $1 ]]; then
if [ "$saveline" ]; then
for ((j=0; j<i; j++)); do echo ${linea[$j]}; done
i=0
else
saveline=1
linea[$((i++))]=$ln
fi
fi
done
}
midgrep "$1"
将此保存为脚本(例如 midgrep.sh)并将您喜欢的任何输出传递给它,如下所示:
$ cat input.txt | ./midgrep.sh stackoverflow
这工作如下:
这种方法的优点是我们只读取一次输入。缺点是我们在每次匹配之间缓冲所有内容——如果每次匹配之间有很多行,那么这些都缓冲到内存中,直到我们遇到下一个匹配。
这也使用 bash=~
正则表达式运算符来保持这个纯 bash。但是,如果您对此更满意,可以将其替换为 grep。
使用perl:
perl -00 -lne '
chomp(my @arr = split /stackoverflow/);
print join "\nstackoverflow", @arr[1 .. $#arr -1 ]
' file.txt | tee newfile.txt
这背后的想法是使用“stackoverflow”字符串将整个输入文件的数组输入到块中以进行拆分。接下来,我们使用连接“stackoverflow”将第二次出现打印到最后一个 -1。