From any kind of scalar, what regex could I use to match the first five lines of it and discard the rest?
8 Answers
Odd request, but this should do it:
#!/usr/bin/perl
use strict;
use warnings;
my $s = join '', map { "$_\n" } 1 .. 9;
my ($first) = $s =~ /^((?:.*\n){0,5})/;
my ($last) = $s =~ /((?:.*\n){0,5})$/;
print "first:\n${first}last:\n$last";
A more common solution would be something like this:
#!/usr/bn/perl
use strict;
use warnings;
#fake a file for the example
my $s = join '', map { "$_\n" } 1 .. 9;
open my $fh, "<", \$s
or die "could not open in memory file: $!";
my @first;
while (my $line = <$fh>) {
push @first, $line;
last if $. == 5;
}
#rewind the file just in case the file has fewer than 10 lines
seek $fh, 0, 0;
my @last;
while (my $line = <$fh>) {
push @last, $line;
#remove the earliest line if we have to many
shift @last if @last == 6;
}
print "first:\n", @first, "last:\n", @last;

- 64,182
- 22
- 135
- 226
-
how would one apply this to the last 5 lines instead? – Tanami Apr 22 '09 at 11:40
-
1my ($last_five_lines) = $s =~ /((?:.*\n){5})\z/; – Hynek -Pichi- Vychodil Apr 22 '09 at 12:02
-
You need a {0,5} modifier, otherwise it will reject a 4 line string (unless that is what you want). – Chas. Owens Apr 22 '09 at 12:20
Why don't you just use head
for that?

- 114,645
- 34
- 221
- 317
-
If your big string is inside a Perl program and you don't want to create temp files, you can't do this. – brian d foy Apr 22 '09 at 19:02
-
@brian d foy Hmm, not true, you could open a bi-directional pipe to head; it would be foolish, but you can do it. – Chas. Owens Apr 22 '09 at 19:43
-
1Are you sure you can open a bi-directional pipe? Perl runs in a lot of places, you know. :) – brian d foy Apr 24 '09 at 19:06
You don't need a regex. Just open a filehandle on a reference to the scalar then do the same things that you would for any other sort of filehandle:
my $scalar = ...;
open my($fh), "<", \ $scalar or die "Could not open filehandle: $!";
foreach ( 1 .. 5 )
{
push @lines, scalar <$fh>;
}
close $fh;
$scalar = join '', @lines;

- 129,424
- 31
- 207
- 592
-
Brian - is that a typo where you refer to $fh twice in the open() call? – Alnitak Apr 23 '09 at 09:04
my ($first_five) = $s =~ /\A((?:.*\n){5})/;
my ($last_five) = $s =~ /((?:.*\n){5})\z/;

- 26,174
- 5
- 52
- 73
As Brian says, you can use head
or tail
pretty easily for either problem (first 5 lines or last 5 lines).
But now I'm wondering if I even understand your question correctly. When you say "for any kind of scalar", do you mean that (for whatever reason) the file is already in a scalar?
If not, I think that the best solution is no regex at all. Use $.
and either read the file normally or backwards. To read backwards, you can try File::ReadBackwards
or File::Bidirectional
.

- 19,459
- 7
- 57
- 79
People are missing some key flags:
/(?m)((?:^.*\n?){1,5})/
Without the multi-line flag, it's only going to look at the first line. Also by making the \n
optional, we can take the first five lines, regardless of a newline at the end of the fifth.

- 29,660
- 2
- 47
- 102
Why not just use split with a limit, it's designed for this purpose:
my @lines = (split /\n/, $scalar, 6)[0..4];
If you want that back as a single scalar with five lines, join it back up:
my $scalar = join('\n', @lines) . "\n";

- 7,575
- 3
- 25
- 18
use strict;
my $line; #Store line currently being read
my $count=$ARGV[1]; # How many lines to read as passed from command line
my @last; #Array to store last count lines
my $index; #Index of the line being stored
#Open the file to read as supplied from command line
open (FILE,$ARGV[0]);
while ($line=<FILE>)
{
$index=$.%$count; # would help me in filter just $count records of the file
$last[$index]=$line; #store this value
}
close (FILE);
#Output the stored lines
for (my $i=$index+1;$i<$count;$i++)
{
print ("$last[$i]");
}
for (my $i=$0;$i<=$index;$i++)
{
print ("$last[$i]");
}

- 1
- 1