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

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

How can I extract a predetermined range of lines from a text file on Unix?

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

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


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

从SED手册:

p -
Print out the pattern space (to the standard output). This command is usually only used in conjunction with the -n command-line option.

n -
If auto-print is not disabled, print the pattern space, then, regardless, replace the pattern space with the next line of input. If
there is no more input then sed exits without processing any more
commands.

q -
Exit sed without processing any more commands or input.
Note that the current pattern space is printed if auto-print is not disabled with the -n option.

Addresses in a sed script can be in any of the following forms:

number
Specifying a line number will match only that line in the input.

An address range can be specified by specifying two addresses
separated by a comma (,). An address range matches lines starting from
where the first address matches, and continues until the second
address matches (inclusively).


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

其中1622416482是起始行号和结束行号(包括首尾行号)。这是一个索引。-n禁止将输入作为输出进行回送,您显然不希望这样做;数字表示要使以下命令运行的行的范围;命令p打印出相关行。


使用头/尾相当简单:

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

使用SED:

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

使用AWK:

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

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

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

可选地:

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

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


对于awk,还有另一种方法:

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

如果文件很大,那么在读取最后一行之后,它对exit是有好处的。这样,它就不会不必要地读取以下行:

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

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

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

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


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

应该有技巧。这种方法的缺点是,您需要执行算术来确定tail的参数,并考虑是否希望"between"包括结束行。


我正准备发布头尾戏法,但实际上我可能只会启动Emacs。;-)

  • esc x转到行reTabbkbd 16224
  • 标记(ctrl spaceakbd)
  • esc x转到行reTabbkbd 16482
  • esc wakbd
  • 打开新的输出文件ctl-y节约

    让我看看发生了什么。


    快速和肮脏:

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

    也许不是最好的方法,但它应该有效。

    btw:259=16482-16224+1。


    我写了一个名为splitter的haskell程序,它可以做到这一点:阅读我的发布博客文章。

    您可以使用以下程序:

    1
    $ cat somefile | splitter 16224-16482

    这就是一切。您需要Haskell来安装它。公正:

    1
    $ cabal install splitter

    你就完了。我希望你觉得这个程序有用。


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

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

    例如

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

    $的意思是"最后一行",所以第一个命令让sed打印所有以16224开头的行,第二个命令让sed在打印完16428之后退出。(在Boxxar的解决方案中添加1用于q范围似乎不必要。)

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


    即使我们可以这样做来检查命令行:

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

    例如:

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


    使用红宝石:

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

    我会用:

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

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


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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    #!/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.
    \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

    这可能适用于您(GNU SED):

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

    或者利用bash:

    1
    2
    sed -n $'16224,16482w newfile
    16482q' file

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

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

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


    接受答案中的-n有效。这是另一种方式,以防你有兴趣。

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

    执行以下操作:

  • 管道输入文件内容(或根据需要输入文本)。
  • SED选择给定的行,打印它
  • d必须删除行,否则,sed将假定所有行最终都将被打印。也就是说,如果没有d,您将得到由所选行打印的所有行两次,因为您有$linenum p部分要求打印它。我很确定-n和这里的d基本上是一样的。

  • 因为我们讨论的是从文本文件中提取文本行,所以我将给出一个特殊的例子,在这个例子中,您希望提取与某个模式匹配的所有行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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。此外,如果您知道两个模式(最好在文本中是唯一的),则可以使用匹配项指定范围的开始行和结束行。

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

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

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

    基于此答案,如果您要还原的表缺少"drop table if exists",则需要在使用新文件之前从该文件底部删除几行以防止删除下一个表。

    详细信息也可以在这里找到


    推荐阅读