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

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

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

606470 次浏览

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

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

另外:

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

编辑:-只是增加解释,你使用Head -n 16482显示前16482行,然后使用Tail -n 258从第一个输出中得到最后258行。

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

sed手册:

< p > # EYZ0 - 打印出模式空间(到标准输出)。该命令通常只与-n命令行选项配合使用 < p > # EYZ0 - 如果没有禁用自动打印,则打印模式空间,然后无论如何,将模式空间替换为下一行输入。如果 没有更多的输入,然后sed退出,不再进行任何处理 命令。< / p > < p > # EYZ0 - 退出sed,不再处理任何命令或输入。 注意,如果没有使用-n选项禁用自动打印,则打印当前的模式空间

and

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

< >强多 指定行号将只匹配输入中的该行

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

 # print section of file based on line numbers
sed -n '16224 ,16482p'               # method 1
sed '16224,16482!d'                 # method 2
perl -ne 'print if 16224..16482' file.txt > new_file.txt

又快又脏:

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

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

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

# EYZ0

使用head/tail非常简单:

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

使用sed:

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

使用awk:

awk 'NR>=16224&&NR<=16482' in.sql > out.sql
sed -n '16224,16482 p' orig-data-file > new-file

其中16224、16482是起始行号和结束行号,包括。这是1索引。-n抑制将输入回显为输出,这显然不是您想要的;数字表示要执行以下命令的行范围;命令p打印出相关的行。

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

应该能行。这种方法的缺点是,您需要做算术来确定tail的参数,并考虑是否希望“between”包含结束行。

我正准备发布头部/尾部技巧,但实际上我可能只是启动emacs。: -)

  1. esc-x到线受潮湿腐烂 16224
  2. 马克(# EYZ0 - # EYZ1)
  3. esc-x到线受潮湿腐烂 16482
  4. # EYZ0 - # EYZ1

打开新的输出文件,ctrl -y 保存< / p >

让我看看发生了什么。

我会用:

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

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

我写了一个名为分束器的Haskell程序,它做的就是:有一个阅读我发布的博客文章

您可以使用该程序如下:

$ cat somefile | splitter 16224-16482

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

$ cabal install splitter

这样就做完了。我希望这个程序对您有用。

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

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

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

例如:

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

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

用法:$ pinch filename起始行结束行

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

使用ruby:

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

这可能为你工作(GNU sed):

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

或者利用bash:

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

接受答案中的-n起作用。如果你有兴趣的话,还有另一种方法。

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

它的作用如下:

  1. 管道插入文件的内容(或根据需要输入文本)。
  2. Sed选择给定的行,打印它
  3. D必须删除行,否则sed将假定最终打印所有行。也就是说,如果没有d,你将得到被选中行打印的所有行打印两次,因为你有${linenum}p部分要求打印它。我很确定-n和这里的d是一样的。

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

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

我想把一个列表分成不同的文件夹,找到最初的问题和答案,这是一个有用的步骤。(分裂命令不是旧操作系统上的选项,我必须将代码移植到)。

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

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

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

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

站在拳击手的肩膀上,我喜欢这样:

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

如。

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

$表示“最后一行”,因此第一个命令使sed打印从行16224开始的所有行,第二个命令使sed退出打印行16428。(在boxxar的解决方案中为# eyz6 -范围添加1似乎没有必要。)

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

艾德:使用

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

-s抑制诊断输出;实际的命令在一个here-string中。具体来说,16224,16482p在所需的行地址范围上运行p(打印)命令。

只是对上面给出的3个解决方案进行了基准测试,这对我来说是可行的:

  • awk
  • sed
  • “头+ tail"

3个解决方案的积分归属:

  • @boxxar
  • @avandeursen
  • @wds
  • @manveru
  • @sibaz
  • @SOFe
  • @fedorqui“所以停止伤害”
  • @Robin A. Meade

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

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

38gb, 1040万行。

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


下注吧!


从文件开始获取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

# EYZ0赢了。在技术上,sed和“头+尾”并列第二。


在文件的前三分之一处获得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

# EYZ0赢了。后面跟着“头+尾”;最后,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

同样的结果。

# EYZ0赢了。后面跟着“头+尾”;最后,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

突然,一个转折!

# EYZ0赢了。接着是awk,最后是sed。


(几个小时后……)

对不起伙计们!

我上面的分析是做分析时一个基本缺陷的例子。

缺陷在于不深入了解用于分析的资源。

在本例中,我使用了一个日志文件来分析其中特定行数的搜索性能。

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

我的错误在于假定日志文件中的内容具有某种同质性。

实际情况是,长行更频繁地出现在文件的末尾。

因此,对于一种给定的技术,较长的搜索(接近文件末尾)更好的明显结论可能是有偏见的。事实上,这种技术在处理较长的队伍时可能会更好。还有待证实。

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

我相对较大的文件 # EYZ0 < / p >

 1  a
2  b
3  c
4  d
5  e
6  f
7  g
8  h
9  i
10  j
11  k

我想要的特定行号: # EYZ0 < / p >

 10
11
4
9

打印这些行号,执行以下操作。 # EYZ0 < / p >

上面所做的是头n行,然后采取最后一行使用尾巴

如果您希望行号按顺序排列,首先sort (is -n numeric sort),然后获取行。

# EYZ0

 4  d
9  i
10  j
11  k

那些试图为head | tail组合计算间隔的人想太多了。

以下是如何获得“16224—16482”的方法;Range而不计算任何东西:

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

解释:

  • +指示head/tail命令"上到 /# EYZ4"(分别)指定行号从文件开始算起

  • 同样,-指示他们"上到 / "(分别)指定行号从文件末尾算起

  • 上面所示的解决方案只是简单地先使用head,到'把所有的东西都放在最上面',然后是tail,到'所有数字都要从下往上',从而定义了我们感兴趣的范围(不需要计算间隔)。

也许,你会给这个简陋的脚本一个机会;-)

#!/usr/bin/bash


# Usage:
#   body n m|-m


from=$1
to=$2


if [ $to -gt 0 ]; then
# count $from the begin of the file $to selected line
awk "NR >= $from && NR <= $to {print}"
else
# count $from the begin of the file skipping tailing $to lines
awk '
BEGIN   {lines=0; from='$from'; to='$to'}
{++lines}
NR >= $from {line[lines]=$0}
END     {for (i = from; i < lines + to + 1; i++) {
print line[i]
}
}'
fi

输出:

$ seq 20 | ./body.sh 5 15
5
6
7
8
9
10
11
12
13
14
15


$ seq 20 | ./body.sh 5 -5
5
6
7
8
9
10
11
12
13
14
15

我已经为sedperlhead+tail编译了一些最高评级的解决方案,加上我自己的awk代码,并通过管道关注性能,同时使用LC_ALL=C确保所有候选人尽可能快,分配2秒的睡眠间隙。

差距是显而易见的:

   abs time    awk/app speed ratio
----------------------------------
0.0672 sec :   1.00x mawk-2
0.0839 sec :   1.25x gnu-sed
0.1289 sec :   1.92x perl
0.2151 sec :   3.20x gnu-head+tail

还没有机会测试这些工具的pythonBSD变体。

 (fg && fg && fg && fg) 2>/dev/null;
echo;
( time ( pvE0 < "${m3t}"
| LC_ALL=C  mawk2 '


BEGIN {
_=10420001-(\
__=10420256)^(FS="^$")
} _<NR {
print


if(__==NR) { exit }
     

}' ) | pvE9) | tee >(xxh128sum >&2) | LC_ALL=C gwc -lcm | lgp3 ;
sleep 2;
(fg && fg && fg && fg) 2>/dev/null
echo;
( time ( pvE0 < "${m3t}"
| LC_ALL=C gsed -n '10420001,10420256p;10420256q'
    

) | pvE9 ) |  tee >(xxh128sum >&2) |  LC_ALL=C gwc -lcm  | lgp3 ;
sleep  2; (fg && fg && fg && fg) 2>/dev/null
echo
( time ( pvE0 < "${m3t}"
| LC_ALL=C perl -ne 'print if 10420001..10420256'
    

) | pvE9 ) |  tee >(xxh128sum >&2) |  LC_ALL=C gwc -lcm | lgp3 ;
sleep  2; (fg && fg && fg && fg) 2>/dev/null
echo
( time ( pvE0 < "${m3t}"
| LC_ALL=C ghead -n +10420256
| LC_ALL=C gtail -n +10420001
) | pvE9 ) |  tee >(xxh128sum >&2) |  LC_ALL=C gwc -lcm  | lgp3 ;




in0: 1.51GiB 0:00:00 [2.31GiB/s] [2.31GiB/s] [============> ] 81%
out9: 42.5KiB 0:00:00 [64.9KiB/s] [64.9KiB/s] [ <=> ]
( pvE 0.1 in0 < "${m3t}" | LC_ALL=C mawk2 ; )
     

0.43s user 0.36s system 117% cpu 0.672 total
256   43487   43487


54313365c2e66a48dc1dc33595716cc8  stdin


out9: 42.5KiB 0:00:00 [51.7KiB/s] [51.7KiB/s] [ <=> ]
in0: 1.51GiB 0:00:00 [1.84GiB/s] [1.84GiB/s] [==========> ] 81%


( pvE 0.1 in0 < "${m3t}" |LC_ALL=C gsed -n '10420001,10420256p;10420256q'; )
  

0.68s user 0.34s system 121% cpu 0.839 total
256   43487   43487


54313365c2e66a48dc1dc33595716cc8  stdin




in0: 1.85GiB 0:00:01 [1.46GiB/s] [1.46GiB/s] [=============>] 100%
out9: 42.5KiB 0:00:01 [33.5KiB/s] [33.5KiB/s] [  <=> ]


( pvE 0.1 in0 < "${m3t}" | LC_ALL=C perl -ne 'print if 10420001..10420256'; )
     

1.10s user 0.44s system 119% cpu 1.289 total
256   43487   43487


54313365c2e66a48dc1dc33595716cc8  stdin


in0: 1.51GiB 0:00:02 [ 728MiB/s] [ 728MiB/s] [=============> ] 81%
out9: 42.5KiB 0:00:02 [19.9KiB/s] [19.9KiB/s] [ <=> ]


( pvE 0.1 in0 < "${m3t}"
| LC_ALL=C ghead -n +10420256
| LC_ALL=C gtail -n ; )
  

1.98s user 1.40s system 157% cpu 2.151 total
256   43487   43487


54313365c2e66a48dc1dc33595716cc8  stdin

在您的情况下,您可以使用sed命令,这非常快。

如前所述,让我们假设范围是:在16224和16482行之间

#get the lines from 16224 to 16482 and prints the values into filename.txt file
sed -n '16224 ,16482p' file.txt > filename.txt
    

#Additional Info to showcase other possible scenarios:
    

#get the 16224 th line and writes the value to filename.txt


sed -n '16224p' file.txt > filename.txt


#get the 16224 and 16300 line values only and write to filename.txt.


sed -n '16224p;16300p;' file.txt > filename.txt