繁体   English   中英

如何在Unix中打印文件中的特定行?

[英]How can I print specific lines from a file in Unix?

我想从Unix中的文本文件中打印某些行。 要打印的行号列在另一个文本文件中(每行一个)。

有没有使用Perl或shell脚本快速完成此操作的方法?

假设要打印的行号已排序。

open my $fh, '<', 'line_numbers' or die $!;
my @ln = <$fh>;
open my $tx, '<', 'text_file' or die $!;
foreach my $ln (@ln) {
  my $line;
  do {
    $line = <$tx>;
  } until $. == $ln and defined $line;
  print $line if defined $line;
}
$ cat numbers
1
4
6
$ cat file
one
two
three
four
five
six
seven
$ awk 'FNR==NR{num[$1];next}(FNR in num)' numbers file
one
four
six

您可以通过在基本while(<>)块的上下文中使用eof来避免某些其他答案(排序行的要求)的限制。 这将告诉您何时停止读取行号并开始读取数据。 请注意,您需要重置$. 当切换发生时。

# Usage: perl script.pl LINE_NUMS_FILE DATA_FILE

use strict;
use warnings;

my %keep;
my $reading_line_nums = 1;

while (<>){
    if ($reading_line_nums){
        chomp;
        $keep{$_} = 1;
        $reading_line_nums = $. = 0 if eof;
    }
    else {
        print if exists $keep{$.};    
    }
}

cat -n foo | 加入foo2 - | cut -d“” - f2-

其中foo是包含要打印的行的文件,foo2是行号的文件

这是一种在Perl中执行此操作的方法,不会产生任何诽谤,因此程序的内存占用量与两个文件的大小无关(它确实假定要打印的行号已排序):

#!/usr/bin/perl

use strict; use warnings;
use autodie;

@ARGV == 2
    or die "Supply src_file and filter_file as arguments\n";

my ($src_file, $filter_file) = @ARGV;

open my $src_h, '<', $src_file;
open my $filter_h, '<', $filter_file;

my $to_print = <$filter_h>;

while ( my $src_line = <$src_h> ) {
    last unless defined $to_print;
    if ( $. == $to_print ) {
        print $src_line;
        $to_print = <$filter_h>;
    }
}

close $filter_h;
close $src_h;

生成源文件:

C:\>  perl -le "print for aa .. zz" > src

生成过滤器文件:

C:\> perl -le "print for grep { rand > 0.75 } 1 .. 52" > filter
C:\> cat filter
4
6
10
12
13
19
23
24
28
44
49
50

输出:

C:\> f src filter
ad
af
aj
al
am
as
aw
ax
bb
br
bw
bx

要处理未排序的过滤器文件,您可以修改while循环:

while ( my $src_line = <$src_h> ) {
    last unless defined $to_print;
    if ( $. > $to_print ) {
        seek $src_h, 0, 0;
        $. = 0;
    }
    if ( $. == $to_print ) {
        print $src_line;
        $to_print = <$filter_h>;
    }
}

如果过滤器文件的内容相当随机,这将浪费大量时间,因为它将继续倒带到源文件的开头。 在这种情况下,我建议使用Tie :: File

我不会这样用大文件,但(未经测试):

open(my $fh1, "<", "line_number_file.txt") or die "Err: $!";
chomp(my @line_numbers = <$fh1>);
$_-- for @line_numbers;
close $fh1;

open(my $fh2, "<", "text_file.txt") or die "Err: $!";
my @lines = <$fh2>;

print @lines[@line_numbers];
close $fh2;

我这样做:

#!/bin/bash
numbersfile=numbers
datafile=data

while read lineno < $numbersfile; do
    sed -n "${lineno}p" datafile
done

我的方法的缺点是它会产生很多进程,所以它会比其他选项慢。 但它的可读性更高。

这是使用bash和sed的简短解决方案

sed -n -e "$(cat num |sed 's/$/p/')" file

num是数字文件,文件是输入文件(在OS / X Snow leopard上测试)

$ cat num
1
3
5

$ cat file
Line One
Line Two
Line Three
Line Four
Line Five

$ sed -n -e "$(cat num |sed 's/$/p/')" file
Line One
Line Three
Line Five
$ cat input
every
good
bird
does
fly

$ cat lines
2
4

$ perl -ne 'BEGIN{($a,$b) = `cat lines`} print if $.==$a .. $.==$b' input
good
bird
does

如果对于单线程来说太多了,请使用

#! /usr/bin/perl

use warnings;
use strict;

sub start_stop {
  my($path) = @_;
  open my $fh, "<", $path
    or die "$0: open $path: $!";

  local $/;
  return ($1,$2) if <$fh> =~ /\s*(\d+)\s*(\d+)/;
  die "$0: $path: could not find start and stop line numbers";
}

my($start,$stop) = start_stop "lines";

while (<>) {
  print if $. == $start .. $. == $stop;
}

Perl的神奇开放允许创造性的可能性,如

$ ./lines-between 'tac lines-between|'
  print if $. == $start .. $. == $stop;
while (<>) {

以下是使用Tie :: File执行此操作的方法:

#!/usr/bin/perl

use strict; use warnings;
use autodie;
use Tie::File;

@ARGV == 2
    or die "Supply src_file and filter_file as arguments\n";

my ($src_file, $filter_file) = @ARGV;

tie my @source, 'Tie::File', $src_file, autochomp => 0
    or die "Cannot tie source '$src_file': $!";

open my $filter_h, '<', $filter_file;

while ( my $to_print = <$filter_h> ) {
    print $source[$to_print - 1];
}

close $filter_h;

untie @source;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM