[英]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.