快速的UNIX命令在文件中间显示特定行?


206

尝试调试服务器问题,而我唯一的日志文件是20GB日志文件(甚至没有时间戳!为什么人们将其System.out.println()用作日志记录?在生产中?!)

使用grep,我找到了要查看的文件区域,行347340107。

除了做类似的事情

head -<$LINENUM + 10> filename | tail -20 

...这将需要head通读日志文件的前三亿四千七百万行,是否有一种快速简便的命令将347340100-347340200行(例如)转储到控制台?

更新我完全忘记了grep可以在比赛中打印上下文...这很好。谢谢!


我想grep必须搜索整个文件,必须有一种不那么密集的cpu方式来执行此操作。
ojblass

Answers:


69

使用GNU-grep,您可以说

grep --context = 10 ...

7
或更具体地说,前10行:grep -B 10 ...或后10行:grep -A 10 ...
Boy Baukema,2012年

17
此命令不起作用,在sed -n'<start>,<end> p'以下起作用
Basav 2013年

5
实际上,这不是您想要的,因为即使匹配位于最高位,它也会处理整个文件。在这一点上,头/尾或尾/头组合更为有效。
Sklivvz

3
这根本不能满足所问的问题,因为这不能提供按要求输出特定的方法。
克里斯·拉西斯

1
这实际上不是所要的。@matt b,你为什么不接受这个答案?
user1271772

390

如果您知道行号,但没有别的,我找到了另外两个解决方案(不可能使用grep):

假设您需要20至40行,

sed -n '20,40p;41q' file_name

要么

awk 'FNR>=20 && FNR<=40' file_name

6
+1:尽管您可能想在打印后退出。如果文件很大,可能会提供一些性能上的好处。
jaypal singh 2014年


2
sed -n '20,40p; 41q'file_name然后退出。
Snigdha Batra

1
具体来说,这些是开始和结束行号。如果你是在一个更大的文件,这将是“12345678,12345699p”
代号Abominator

1
除了@CodeAbominator的注释,41q指令sed还要在line 上退出41
布里斯

116
# print line number 52
sed -n '52p' # method 1
sed '52!d' # method 2
sed '52q;d' # method 3,  efficient on large files 

方法3对大文件有效

显示特定行的最快方法


我试图弄清楚如何使方法3适应使用范围而不是单行,但是恐怕我的sed-foo无法完成任务。
熊加米奥夫

9
@XiongChiamiov sed -n'1,500p; 501q'打印1-500怎么样?
山姆

3
前两行/方法效率较低的原因是,它们继续处理52行之后的所有行,直到结束,而#3在打印52行之后停止
。– flow2k

1
通过解释所有论点的作用,该答案将是有益的。
Bram Vanroy

25

不,没有,文件不是行可寻址的。

没有固定时间的方法来查找文本文件中第n行的开头。您必须浏览文件并计数换行符。

使用您必须完成的最简单/最快的工具。对我来说,使用head使不是更有意义grep,因为后者的方式更加复杂。我并不是说“ grep很慢”,的确不是,但是如果它比head在这种情况下更快,我会感到惊讶。head基本上,那将是一个bug 。


2
除非行是固定宽度(以字节为单位),否则您不知道从文件开头算起新行字符就不会将文件指针移动到何处。
约瑟夫·拉斯特

这不能为问题提供答案。要批评或要求作者澄清,请在其帖子下方发表评论。
2015年

@exhuma你是对的。我改写了。七年前,我感到沮丧。:)
放松身心

20

关于什么:

tail -n +347340107 filename | head -n 100

我没有测试它,但是我认为那是可行的。


不可以,根据版本和操作系统的不同,尾部通常不能超过256个最后千字节。
AnttiRytsölä15年

💪yessire miller
dctremblay

13

我宁愿只是进入less

  • 键入50%以转到文件的一半,
  • 43210G 去43210行
  • :43210 做同样的

诸如此类的东西。

更好的是:v在该位置单击以开始编辑(当然是在vim中!)。现在,请注意vim具有相同的键绑定!


12

我首先将文件分成几个较小的文件

$ split --lines=50000 /path/to/large/file /path/to/output/file/prefix

然后grep生成的文件。


同意,中断该日志并创建一个cron作业以正确执行此操作。使用logrotate或类似方法防止它们变大。
Tanj

9

您可以使用ex标准Unix编辑器(现在是Vim的一部分)的命令,例如

  • 显示一行(例如第二行):

    ex +2p -scq file.txt

    对应的sed语法: sed -n '2p' file.txt

  • 行数范围(例如2-5行):

    ex +2,5p -scq file.txt

    sed语法: sed -n '2,5p' file.txt

  • 从给定的行到末尾(例如文件的第5到末尾):

    ex +5,p -scq file.txt

    sed语法: sed -n '2,$p' file.txt

  • 多行范围(例如2-4和6-8行):

    ex +2,4p +6,8p -scq file.txt

    sed语法: sed -n '2,4p;6,8p' file.txt

可以使用以下测试文件来测试以上命令:

seq 1 20 > file.txt

说明:

  • +-c后跟命令-读取文件后执行(vi / vim)命令,
  • -s -静音模式,也将当前端子用作默认输出,
  • q接下来-c是退出编辑器的命令(!例如,添加以强制退出-scq!)。

6

如果您要读取的行号是100

head -100 filename | tail -1

6

得到 ack

Ubuntu / Debian安装:

$ sudo apt-get install ack-grep

然后运行:

$ ack --lines=$START-$END filename

例:

$ ack --lines=10-20 filename

来自$ man ack

--lines=NUM
    Only print line NUM of each file. Multiple lines can be given with multiple --lines options or as a comma separated list (--lines=3,5,7). --lines=4-7 also works. 
    The lines are always output in ascending order, no matter the order given on the command line.

1
在我看来,这似乎是此处所有答案中具有最直观语法的命令。
nzn

从2019年1月10日的2.999_06版本开始,该--lines参数已被删除。
燃烧

4

sed也需要读取数据来计数行数。快捷方式唯一可行的方法是在文件中存在上下文/顺序以进行操作。例如,如果有以固定宽度的时间/日期等开头的日志行,则可以使用look unix实用程序对文件中的特定日期/时间进行二进制搜索。


4

x=`cat -n <file> | grep <match> | awk '{print $1}'`

在这里,您将获得匹配发生的行号。

现在您可以使用以下命令打印100行

awk -v var="$x" 'NR>=var && NR<=var+100{print}' <file>

或者您也可以使用“ sed”

sed -n "${x},${x+100}p" <file>

如果您有多个比赛,请在第一个比赛中使用:“ awk'NR == 1 {print $ 1}”,依此类推
Ramana Reddy

2

随着sed -e '1,N d; M q'您将打印从N + 1到M的行。这可能会好一些,grep -C因为它不会尝试将行与模式匹配。


-e在这里是可选的。
flow2k '19

2

基于Sklivvz的答案,这是一个不错的功能,可以将其放入.bash_aliases文件中。从文件正面打印内容时,它对大型文件非常有效。

function middle()
{
    startidx=$1
    len=$2
    endidx=$(($startidx+$len))
    filename=$3

    awk "FNR>=${startidx} && FNR<=${endidx} { print NR\" \"\$0 }; FNR>${endidx} { print \"END HERE\"; exit }" $filename
}

1

要显示<textfile><line#>旁边的一行,只需执行以下操作:

perl -wne 'print if $. == <line#>' <textfile>

如果您想要一种更强大的方法来显示带有正则表达式的行的范围-我不会说grep为什么这样做不好,那应该很明显-这个简单的表达式将以单次通过是处理约20GB文本文件时需要的:

perl -wne 'print if m/<regex1>/ .. m/<regex2>/' <filename>

(提示:如果您的正则表达式包含/其中,请改用类似的m!<regex>!符号)

<filename>从匹配的行开始打印,<regex1>直到(包括)匹配的行为止<regex2>

无需向导即可了解如何进行一些调整以使其变得更加强大。

最后一件事:perl,因为它是一种成熟的语言,所以具有许多隐藏的增强功能,它们有利于提高速度和性能。考虑到这一点,由于它最初是为处理大型日志文件,文本,数据库等而开发的,因此使其成为此类操作的明显选择。


确实,这对我来说似乎不是这样,因为当运行一个perl命令比说的更复杂时,运行2个以上通过管道输送在一起的程序(在页面的更下方),而且,我想您实际上是在说,因为我键入了更多一种解释,需要您阅读,因为同样复杂(或更复杂)的页面没有被水淹没...

请注意,用户要求输入一定范围的行-尽管您的示例可以轻松修改。
Sklivvz 2015年


0

使用perl轻松!如果要从文件获取第1、3和5行,请说/ etc / passwd:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd

1
您说使用awk很容易,但是您是在perl中完成的?
囚犯

0

我很惊讶只有另一个答案(由Ramana Reddy提出)建议在输出中添加行号。以下内容搜索所需的行号并为输出着色。

file=FILE
lineno=LINENO
wb="107"; bf="30;1"; rb="101"; yb="103"
cat -n ${file} | { GREP_COLORS="se=${wb};${bf}:cx=${wb};${bf}:ms=${rb};${bf}:sl=${yb};${bf}" grep --color -C 10 "^[[:space:]]\\+${lineno}[[:space:]]"; }

带有代码的答案只会被标记为删除。您能否添加一些有关如何解决问题的评论?
格雷厄姆
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.