我需要在我的搜索字符串上方提取一行(例如,上方 19 行)。通常,我会选择
grep -B 19 $search_string $file | ...further processing
但是该脚本也应该适用于 Solaris,其中 grep 不提供 -B 选项。通常,如果我知道前面的行,我可以使用 awk '/begin/,/end/'
打印一堆行。在这种特殊情况下,这是不可能的。我尝试了以下方法:
1) 环形缓冲溶液。
#!/bin/bash
g_a_buffer=( 0 )
g_i_buffer_index=1
while read line
do
g_a_buffer[$((g_i_buffer_index % 20))]=$line
echo $line|grep $search_string > /dev/null
[ $? -eq 0 ] && echo ${g_a_buffer[$(( (g_i_buffer_index + 2) % 20))]}
let "g_i_buffer_index += 1"
done < $file_name
这非常慢。对于大约 40k 行,它需要 1m37s(对于 grep 为 0.005s)
2) Awk 解决方案。我不得不直截了本地说,我是 awk 的极端初学者,很少超越 awk '{print $1}'。 下面一行行不通,但可以让您了解我要实现的目标:
awk '/mySearchString/ {print NR-19}' filename.txt
执行0.118s,速度不错!但我得到的只是一个行号 - 19。我需要的是位于(第 - 19 行)的行的打印输出。经过一番谷歌搜索后,我仍然找不到答案。我承认这一定是一个非常基本的问题,但我似乎在这里碰壁了。
到目前为止我发现的是如何使用 awk(这是一种单行缓冲区)打印前一行,或者使用环形缓冲区但在 awk 中的大量实现。有没有更优雅的方法来做到这一点?
感谢您的帮助!
最佳答案
这是一个需要两次遍历文件的解决方案,因此不是最优的,但在实践中可能表现得很好。 (在 GNU awk 上测试过,但没有明显的原因表明它不能在 Solaris 上运行)。
awk "$(awk '/mySearchString/ { print "NR==" NR-19 }' myInputFile.txt)" myInputFile.txt
由于这需要两次传递,如果您从其他地方通过管道传输输入,您需要将其存储在某个地方的临时文件中。
或者,如果您知道您的搜索字符串在文件中最多出现一次(或者至少您只关心第一次出现),您可以将 awk 与 head 和 tail 结合使用以提取该行:
awk 'NR==1,/mySearchString/' | tail -n 19 | head -n 1
我手头没有合适的文本文件来对其进行基准测试,但我希望它比您的环形缓冲区解决方案好很多。
关于arrays - grep -B 模拟环形缓冲区/awk,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8386665/