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

时间:2008-09-17 13:41:00

标签: unix command-line sed text-processing

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

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

26 个答案:

答案 0 :(得分:708)

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

来自sed manual

  

p -       打印出图案空间(到标准输出)。此命令通常仅与-n命令行选项一起使用。

     

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

     

q -   退出sed而不处理更多命令或输入。   请注意,如果未使用-n选项禁用自动打印,则会打印当前模式空间。

and

  

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

     

<强>号       指定行号将仅匹配输入中的该行。

     

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

答案 1 :(得分:196)

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

其中16224,16482是起始行号和结束行号,包括在内。这是1索引的。 -n禁止将输入作为输出回显,这显然是你不想要的;数字表示使以下命令操作的行数范围;命令p打印出相关的行。

答案 2 :(得分:81)

使用头/尾非常简单:

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

答案 3 :(得分:28)

您可以使用'vi'然后使用以下命令:

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

或者:

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

编辑: - 只是为了添加说明,您可以使用 head -n 16482 显示第一个16482行,然后使用 tail -n 258 来获取最后的258行第一个输出。

答案 4 :(得分:22)

awk还有另一种方法:

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

如果文件很大,那么在阅读完最后一行后exit可能会很好。这样,它就不会不必要地读取以下行:

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

答案 5 :(得分:17)

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

答案 6 :(得分:8)

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

答案 7 :(得分:5)

sed -n '16224,16482p' < dump.sql

答案 8 :(得分:5)

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

应该做的伎俩。这种方法的缺点是你需要做算术来确定尾部的参数,并考虑你是否希望'between'包括结束行。

答案 9 :(得分:3)

即使我们可以执行此操作来检查命令行:

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

例如:

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

答案 10 :(得分:3)

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

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

例如

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

$的意思是“最后一行”,因此第一个命令使sed打印从行16224开始的所有行,第二个命令使sed退出打印行16428之后。 (在boxxar解决方案的1范围内添加q似乎是不必要的。)

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

答案 11 :(得分:3)

使用ruby:

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

答案 12 :(得分:3)

我编写了一个名为splitter的Haskell程序,它正是这样做的:有一个read through my release blog post

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

$ cat somefile | splitter 16224-16482

这就是它的全部内容。您将需要Haskell来安装它。只是:

$ cabal install splitter

你完成了。我希望你发现这个程序很有用。

答案 13 :(得分:3)

我准备发布头/尾技巧,但实际上我可能只是发布了emacs。 ; - )

  1. esc - x goto-line ret 16224
  2. mark( ctrl - space
  3. esc - x goto-line ret 16482
  4. ESC - 瓦特
  5. 打开新的输出文件ctl-y 保存

    让我看看发生了什么。

答案 14 :(得分:3)

又快又脏:

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

可能不是最好的方法,但应该有效。

BTW:259 = 16482-16224 + 1.

答案 15 :(得分:2)

我会用:

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

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

答案 16 :(得分:2)

试图围绕计算 head | tail 组合的间隔的人想得太多了。

以下是无需计算任何内容即可获得“16224 -- 16482”范围的方法:

cat file | head -n +16482 | tail -n +16224

说明:

  • + 指示 head/tail 命令“go up to / start from”(分别)指定的行号从文件开头算起

  • 类似地,- 指示他们“上升到 / 开始”(分别)指定的行号作为从文件末尾算起

  • 上面显示的解决方案简单地首先使用 head,以“将所有内容保持在最高数字”,然后使用 tail 第二,以“保留从底部数字向上的所有内容',从而定义我们感兴趣的范围(无需计算间隔)。

答案 17 :(得分:1)

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

用法:$ pinch filename start-line end-line

#!/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

答案 18 :(得分:1)

这可能适合你(GNU sed):

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

或利用bash:

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

答案 19 :(得分:1)

我想从使用变量的脚本中做同样的事情,并通过在$变量周围加上引号来将变量名称与p分开来实现:

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

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

答案 20 :(得分:0)

接受答案中的-n工作。如果你有倾向,这是另一种方式。

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

执行以下操作:

  1. 在文件内容中输入管道(或者根据需要输入文本)。
  2. sed选择给定的行,将其打印
  3. d需要删除行,否则sed将假定最终将打印所有行。即,如果没有d,您将获得所选行打印的所有行,因为您有$ {linenum} p部件要求打印它。我很确定-n基本上和d在做同样的事情。

答案 21 :(得分:0)

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

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

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

sed -n '/BEGIN_MARK/,/END_MARK/p' myfile

答案 22 :(得分:0)

使用ed:

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

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

答案 23 :(得分:0)

只需对上述给出的3个解决方案进行基准测试,即可对我有效:

  • awk
  • sed
  • “头+尾”

这3个解决方案的信用额归功于:

  • @boxxar
  • @avandeursen
  • @wds
  • @manveru
  • @sibaz
  • @SOFe
  • @fedorqui'停止伤害'
  • @Robin A. Meade

我正在使用在服务器中找到的巨大文件:

# wc fo2debug.1.log
   10421186    19448208 38795491134 fo2debug.1.log

38 Gb,1,040万条线路。

是的,我有一个logrotate问题。 :))


下注!


从文件开头获取256行。

# time sed -n '1001,1256p;1256q' fo2debug.1.log | wc -l
256

real    0m0,003s
user    0m0,000s
sys     0m0,004s

# time head -1256 fo2debug.1.log | tail -n +1001 | wc -l
256

real    0m0,003s
user    0m0,006s
sys     0m0,000s

# time awk 'NR==1001, NR==1256; NR==1256 {exit}' fo2debug.1.log | wc -l
256

real    0m0,002s
user    0m0,004s
sys     0m0,000s

知道获胜。 sed和“ head + tail”之间的技术联系排名第二。


在文件的前三分之一的末尾获得256行。

# time sed -n '3473001,3473256p;3473256q' fo2debug.1.log | wc -l
256

real    0m0,265s
user    0m0,242s
sys     0m0,024s

# time head -3473256 fo2debug.1.log | tail -n +3473001 | wc -l
256

real    0m0,308s
user    0m0,313s
sys     0m0,145s

# time awk 'NR==3473001, NR==3473256; NR==3473256 {exit}' fo2debug.1.log | wc -l
256

real    0m0,393s
user    0m0,326s
sys     0m0,068s

Sed 赢了。接着是“ head + tail”,最后是awk。


在文件的第二个三分之一的末尾获得256行。

# time sed -n '6947001,6947256p;6947256q' fo2debug.1.log | wc -l
A256

real    0m0,525s
user    0m0,462s
sys     0m0,064s

# time head -6947256 fo2debug.1.log | tail -n +6947001 | wc -l
256

real    0m0,615s
user    0m0,488s
sys     0m0,423s

# time awk 'NR==6947001, NR==6947256; NR==6947256 {exit}' fo2debug.1.log | wc -l
256

real    0m0,779s
user    0m0,650s
sys     0m0,130s

相同的结果。

Sed 赢了。接着是“ head + tail”,最后是awk。


在文件末尾获取256行。

# time sed -n '10420001,10420256p;10420256q' fo2debug.1.log | wc -l
256

real    1m50,017s
user    0m12,735s
sys     0m22,926s

# time head -10420256 fo2debug.1.log | tail -n +10420001 | wc -l
256

real    1m48,269s
user    0m42,404s
sys     0m51,015s

# time awk 'NR==10420001, NR==10420256; NR==10420256 {exit}' fo2debug.1.log | wc -l
256

real    1m49,106s
user    0m12,322s
sys     0m18,576s

突然之间,转弯!

“头+尾” 获胜。随后是awk,最后是sed。


(几个小时后...)

对不起!

我上面的分析最终是一个分析中的基本缺陷的例子。

该缺陷无法深入了解用于分析的资源。

在这种情况下,我使用一个日志文件来分析其中一定数量行的搜索性能。

使用3种不同的技术,在文件的不同点进行搜索,比较每个点的技术性能,并检查结果是否随搜索文件中的点而变化。

我的错误是假设日志文件中的内容存在一定的同质性。

现实情况是,长行在文件末尾出现的频率更高。

因此,显然的结论是,使用给定的技术,较长的搜索(更接近文件的末尾)更好。实际上,当处理更长的线时,此技术可能更好。有待确认的地方。

答案 24 :(得分:0)

我一直在寻找这个问题的答案,但最终我不得不编写自己的有效代码。以上答案都不令人满意。 考虑到您有一个非常大的文件,并且有一些要打印的行号,但这些行号没有按顺序排列。您可以执行以下操作:

我比较大的文件 HKLM

for letter in {a..k} ; do echo $letter; done | cat -n > myfile.txt

我想要的特定行号: 1 a 2 b 3 c 4 d 5 e 6 f 7 g 8 h 9 i 10 j 11 k

shuf -i 1-11 -n 4 > line_numbers_I_want.txt

要打印这些行号,请执行以下操作。 10 11 4 9

上面的做法是将第 n 行作为头,然后使用 tail 取最后一行

如果您希望按顺序排列行号,请先排序(是 -n 数字排序),然后获取行。

awk '{system("head myfile.txt -n " $0 " | tail -n 1")}' line_numbers_I_want.txt

cat line_numbers_I_want.txt | sort -n | awk '{system("head myfile.txt -n " $0 " | tail -n 1")}'

答案 25 :(得分:-3)

我认为这可能是有用的解决方案。如果表名是“person”,您可以使用sed获取恢复表所需的所有行。

sed -n -e '/DROP TABLE IF EXISTS.*`person `/,/UNLOCK TABLES/p' data.sql  > new_data.sql

基于this answer,缺少正在恢复的表的“DROP TABLE IF EXIST”,您需要从新文件底部删除几行,然后再使用它来防止删除下一个文件表

也可以找到详细信息here