如何从Unix上的文本文件中提取预定范围的行?


531

我有一个〜23000行的SQL转储,其中包含多个数据库数据。我需要提取此文件的某个部分(即单个数据库的数据)并将其放置在新文件中。我知道我想要的数据的开始和结束行号。

有谁知道Unix命令(或一系列命令)从文件中提取16224和16482行之间的所有行,然后将它们重定向到新文件中?


既然你提到大的文件,我建议检查意见stackoverflow.com/questions/83329/...
sancho.s ReinstateMonicaCellio

Answers:


792
sed -n '16224,16482p;16483q' filename > newfile

sed手册中

p-打印出图案空间(至标准输出)。该命令通常仅与-n命令行选项结合使用。

n-如果未禁用自动打印,请打印图案空间,然后无论如何都要用下一行输入替换图案空间。如果没有更多输入,则sed退出而不处理任何其他命令。

q-退出sed而不处理任何其他命令或输入。请注意,如果未使用-n选项禁用自动打印,则会打印当前图案空间。

sed脚本中的地址可以采用以下任何形式:

指定行号将匹配输入唯一的那条线。

可以通过指定两个用逗号(,)分隔的地址来指定地址范围。地址范围匹配从第一个地址匹配的行开始,一直持续到第二个地址匹配(包括第二个地址)为止。


3
我很好奇这是否修改了原始文件。我备份了它,以防万一它没有像预期的那样修改原始文件。
安迪·格罗夫

@AndyGroff。要在适当位置修改文件,请使用“ -i”参数。否则它将不会修改文件。
youi 2013年

175
如果像我一样需要在一个非常大的文件上执行此操作,则在下一行添加quit命令会有所帮助。然后是sed -n '16224,16482p;16483q' filename。否则,sed将一直扫描到最后(或者至少是我的版本)。
wds 2013年

7
@MilesRout人们似乎在问“为什么要投票?” 很多时候,也许你的意思是“我不在乎”而不是“没人在乎”
Mark

1
@wds-您的评论很值得得到最高解答。它可以使白天和黑夜有所不同。
sancho.s ReinstateMonicaCellio

203
sed -n '16224,16482 p' orig-data-file > new-file

其中16224,16482是起始行号和结束行号(含)。这是1索引的。 -n抑制将输入作为输出回显,这显然是不希望的;数字表示执行以下命令的行范围;该命令将p打印出相关行。


7
在大文件上,找到所需范围后,上述命令将继续遍历整个文件。一旦范围输出,sed是否有办法停止处理文件?
加里

39
好吧,从这里的答案来看,似乎可以使用以下命令完成在范围结尾处的停止sed -n '16224,16482p;16482q' orig-data-file > new-file
加里

5
您为什么要放置不必要的空间,然后不得不报价?(当然,提出不必要的问题并加以解决是计算机科学一半的本质,但我的意思是除此之外……)
Kaz

92

使用头/尾非常简单:

head -16482 in.sql | tail -258 > out.sql

使用sed:

sed -n '16482,16482p' in.sql > out.sql

使用awk:

awk 'NR>=10&&NR<=20' in.sql > out.sql

1
第二个和第三个选项都可以,但是第一个选项比许多其他选项要慢,因为它使用2个命令,其中1个就足够了。它还需要进行计算才能获得正确的参数tail
乔纳森·勒夫勒

3
值得注意的是,要保持与问题相同的行号,sed命令应该为sed -n 16224,16482p' in.sql >out.sqlawk命令,而awk命令应该为awk 'NR>=16224&&NR<=16482' in.sql > out.sql
sibaz

3
同样值得一提的是,在第一个示例的情况下,head -16482 in.sql | tail -$((16482-16224)) >out.sql计算工作
陷入了低潮

1
即使添加了q选项,第一个在大文件上具有头部和尾部WAYYYY的速度也比sed版本要快。头版即时和sed版本I Ctrl-C一分钟后...谢谢
Miyagi

2
也可以tail -n +16224用来减少计算量
SOFe18年

35

您可以使用“ vi”,然后使用以下命令:

:16224,16482w!/tmp/some-file

或者:

cat file | head -n 16482 | tail -n 258

编辑:-只是为了增加解释,您使用head -n 16482显示前16482行,然后使用tail -n 258从第一个输出中获取最后258行。


2
除了vi,您可以使用ex,而vi是交互式控制台的东西。
Tadeusz A.Kadłubowski10年

1
您不需要cat命令;head可以直接读取文件。这比许多替代方法要慢,因为它使用了2个(如图所示为3个)命令,其中1个就足够了。
乔纳森·莱夫勒

1
@JonathanLeffler你错了。速度非常快。我在几秒钟内(没有cat)从具有500k行的2G文件中提取了200k行,约1G 。其他解决方案至少需要几分钟。而且GNU上最快的变化似乎是tail -n +XXX filename | head XXX
Antonis Christofides

28

还有另一种方法awk

awk 'NR==16224, NR==16482' file

如果文件很大,最好exit读取最后一行。这样,它不会不必要地读取以下行:

awk 'NR==16224, NR==16482-1; NR==16482 {print; exit}' file

awk 'NR==16224, NR==16482; NR==16482 {exit}' file

2
1 +用于使用节省运行时间和资源print; exit。谢谢 !
伯尼·瑞特

第二个示例略有简化:awk 'NR==16224, NR==16482; NR==16482 {exit}' file
Robin A. Meade

太好了,谢谢@ RobinA.Meade!我编辑你的想法在后
fedorqui“所以停止损害”

17
perl -ne 'print if 16224..16482' file.txt > new_file.txt

9
 # print section of file based on line numbers
 sed -n '16224 ,16482p'               # method 1
 sed '16224,16482!d'                 # method 2

6
cat dump.txt | head -16224 | tail -258

应该可以。这种方法的缺点是,您需要进行算术运算以确定出tail的参数,并考虑是否要在“ between”中包含结束行。


4
您不需要cat命令;head可以直接读取文件。这比许多替代方法要慢,因为它使用2个命令(如图所示3个),其中1个就足够了。
乔纳森·莱夫勒

@JonathanLeffler这个答案是最容易阅读和记住的。如果您真正关心性能,那么您一开始就不会使用Shell。好的做法是让特定工具专用于特定任务。此外,可以使用来解析“算术” | tail -$((16482 - 16224))
雪人

6

我站在Boxxar的肩膀上,像这样:

sed -n '<first line>,$p;<last line>q' input

例如

sed -n '16224,$p;16482q' input

$意思是“最后行”,所以第一个命令使sed打印开始的行中的所有行16224和所述第二命令使sed退出打印线16428。(1q在boxxar的解决方案-范围似乎没有必要。)

我喜欢这种变体,因为我不需要两次指定结束行号。而且我测量到使用$不会对性能产生不利影响。



3

快速又肮脏:

head -16428 < file.in | tail -259 > file.out

可能不是最好的方法,但它应该可以工作。

顺便说一句:259 = 16482-16224 + 1。


这比许多替代方法要慢,因为它使用2条命令,其中1条就足够了。
乔纳森·莱夫勒

3

我编写了一个名为splitter的Haskell程序,该程序正是这样做的:请仔细阅读我的发行博客文章

您可以按以下方式使用该程序:

$ cat somefile | splitter 16224-16482

这就是全部。您将需要Haskell进行安装。只是:

$ cabal install splitter

您完成了。我希望您觉得这个程序有用。


是否splitter只有从标准输入读取?从某种意义上说,没关系;cat无论是否执行该命令都是多余的。使用splitter 16224-16482 < somefile或(如果使用文件名参数)splitter 16224-16482 somefile
乔纳森·莱夫勒

3

甚至我们也可以在命令行中进行检查:

cat filename|sed 'n1,n2!d' > abc.txt

例如:

cat foo.pl|sed '100,200!d' > abc.txt

6
您都不需要这两个cat命令。sed完全能够自己读取文件,或者您可以重定向文件中的标准输入。
乔纳森·莱夫勒

3

使用红宝石:

ruby -ne 'puts "#{$.}: #{$_}" if $. >= 32613500 && $. <= 32614500' < GND.rdf > GND.extract.rdf

2

我本来打算发布头尾技巧,但实际上我可能只是启动了emacs。;-)

  1. esc- x转到第ret16224 行
  2. 标记(ctrl- space
  3. esc- x转到第ret16482 行
  4. esc--w

打开新的输出文件,ctl-y保存

让我看看发生了什么。


4
根据我的经验,Emacs在非常大的文件上表现不佳。
格雷格·马特斯

您可以将其作为脚本操作来运行,还是仅是一个交互式选项?
乔纳森·莱夫勒

2

我会用:

awk 'FNR >= 16224 && FNR <= 16482' my_file > extracted.txt

FNR包含从文件中读取的行的记录(行)号。


2

我想使用变量在脚本中执行相同的操作,并通过在$ variable周围加上引号以将变量名与p分开来实现此目的:

sed -n "$first","$count"p imagelist.txt >"$imageblock"

我想将列表拆分到单独的文件夹中,找到了最初的问题并回答了一个有用的步骤。(在我必须移植代码的旧操作系统上,split命令不是一个选项)。


1

我编写了一个小的bash脚本,您可以从命令行运行它,只要您更新PATH以包括其目录即可(或者可以将其放置在PATH中已经包含的目录中)。

用法:$捏文件名开始行结束行

#!/bin/bash
# Display line number ranges of a file to the terminal.
# Usage: $ pinch filename start-line end-line
# By Evan J. Coon

FILENAME=$1
START=$2
END=$3

ERROR="[PINCH ERROR]"

# Check that the number of arguments is 3
if [ $# -lt 3 ]; then
    echo "$ERROR Need three arguments: Filename Start-line End-line"
    exit 1
fi

# Check that the file exists.
if [ ! -f "$FILENAME" ]; then
    echo -e "$ERROR File does not exist. \n\t$FILENAME"
    exit 1
fi

# Check that start-line is not greater than end-line
if [ "$START" -gt "$END" ]; then
    echo -e "$ERROR Start line is greater than End line."
    exit 1
fi

# Check that start-line is positive.
if [ "$START" -lt 0 ]; then
    echo -e "$ERROR Start line is less than 0."
    exit 1
fi

# Check that end-line is positive.
if [ "$END" -lt 0 ]; then
    echo -e "$ERROR End line is less than 0."
    exit 1
fi

NUMOFLINES=$(wc -l < "$FILENAME")

# Check that end-line is not greater than the number of lines in the file.
if [ "$END" -gt "$NUMOFLINES" ]; then
    echo -e "$ERROR End line is greater than number of lines in file."
    exit 1
fi

# The distance from the end of the file to end-line
ENDDIFF=$(( NUMOFLINES - END ))

# For larger files, this will run more quickly. If the distance from the
# end of the file to the end-line is less than the distance from the
# start of the file to the start-line, then start pinching from the
# bottom as opposed to the top.
if [ "$START" -lt "$ENDDIFF" ]; then
    < "$FILENAME" head -n $END | tail -n +$START
else
    < "$FILENAME" tail -n +$START | head -n $(( END-START+1 ))
fi

# Success
exit 0

1
这比许多替代方法要慢,因为它使用2条命令,其中1条就足够了。实际上,由于该wc命令,它两次读取文件,这浪费了磁盘带宽,尤其是在千兆字节文件上。在各种方面,这都有据可查,但它也是工程学的过分杀伤力。
乔纳森·莱夫勒

1

这可能对您有用(GNU sed):

sed -ne '16224,16482w newfile' -e '16482q' file

或利用bash:

sed -n $'16224,16482w newfile\n16482q' file

1

使用ed:

ed -s infile <<<'16224,16482p'

-s抑制诊断输出;实际命令在此处字符串中。具体而言,在所需的行地址范围上16224,16482p运行p(打印)命令。


0

接受答案中的-n起作用。如果您有这种倾向,这是另一种方法。

cat $filename | sed "${linenum}p;d";

这将执行以下操作:

  1. 管道输入文件的内容(或根据需要输入文本)。
  2. sed选择给定的行,然后打印
  3. d是删除行所必需的,否则sed将假定所有行最终都将被打印。也就是说,如果没有d,则将使所选行打印的所有行都打印两次,因为您有$ {linenum} p部分要求打印它。我很确定-n基本上和d在做同样的事情。

3
注意cat file | sed最好写成sed file
fedorqui'SO停止伤害

同样,这只是打印一行,而问题是关于它们的范围。
fedorqui'SO停止伤害

0

由于我们正在谈论从文本文件中提取文本行,因此我将给出一种特殊情况,即您要提取与特定模式匹配的所有行。

myfile content:
=====================
line1 not needed
line2 also discarded
[Data]
first data line
second data line
=====================
sed -n '/Data/,$p' myfile

将打印[Data]行和其余行。如果要从第1行到模式的文本,请键入:sed -n'1,/ Data / p'myfile。此外,如果您知道两个模式(在文本中最好是唯一的),则可以使用匹配项来指定范围的开始和结束行。

sed -n '/BEGIN_MARK/,/END_MARK/p' myfile
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.