Argv File Slurp
Part 1: DOWN THE RABBIT HOLE
Every Perl programmer knows about reading files. Open a filehandle, read the contents, close it. Simple, clean, boring.But what if I told you there's a way to slurp an entire file in one line using nothing but @ARGV trickery and the diamond operator?
Behold:
That's it. One line. No explicit open. No close. No filehandle variable. Just pure, distilled Perl magic.my $content = do { local @ARGV = ($filename); <> };
Is this a good idea? Probably not. Is it fascinating? Absolutely.
Part 2: HOW THE MAGIC WORKS
Let's dissect this beautiful monstrosity piece by piece.The do block executes the code inside and returns the value of the last expression. It creates a scope for our local variable.do { ... }
Here's where it gets interesting. We're temporarily replacing @ARGV with a single-element list containing our filename. The 'local' keyword ensures this change is undone when we leave the block.local @ARGV = ($filename);
The diamond operator! When used without a filehandle, it reads from the files listed in @ARGV. We've just told it there's exactly one "argument"<>
- our filename.
In list context, <> slurps the entire file. The result flows back through the do block and into $content.
It's like we've tricked Perl into thinking the script was invoked with our filename as a command-line argument.
Part 3: CONTEXT MATTERS
The diamond operator behaves differently based on context:Wait, that last one looks like scalar context. Why does it slurp?# Scalar context - reads one line my $line = do { local @ARGV = ($filename); <> }; # List context - slurps entire file my @lines = do { local @ARGV = ($filename); <> }; my $content = do { local @ARGV = ($filename); <> }; # Also list context!
Because we're inside a do block, and Perl evaluates the expression in the context of the assignment. The do block propagates context inward. When assigning to a scalar, Perl uses the last value from the list - but <> still reads everything.
If you want exactly one line:
my $first_line = do { local @ARGV = ($filename); scalar <> };
Part 4: PRACTICAL APPLICATIONS
Okay, party tricks aside, when might this actually be useful?PROCESSING MULTIPLE FILES WITH PER-FILE ANALYSIS:
QUICK ONE-LINER STYLE:my @files = qw(data1.txt data2.txt data3.txt); for my $file (@files) { my $content = do { local @ARGV = ($file); <> }; my $line_count = ($content =~ tr/\n//); my $word_count = scalar(split /\s+/, $content); print "$file: $line_count lines, $word_count words\n"; }
AVOIDING FILEHANDLE POLLUTION:# Imagine this in a larger script where you need a quick slurp my $config = do { local @ARGV = ("$ENV{HOME}/.myapprc"); <> };
When you're in a tight scope and don't want to deal with filehandle management, this pattern is self-contained:
No filehandle leaks. No forgotten closes. The block cleans up after itself.sub get_template { my ($name) = @_; return do { local @ARGV = ("templates/$name.tmpl"); <> }; }
Part 5: PROCESSING FILES SEQUENTIALLY
Here's where @ARGV manipulation really shines - batch processing:The magic variable $ARGV contains the current filename being processed. Perl automatically opens each file in sequence.my @logfiles = glob("logs/*.log"); # Process all files as if they were one stream local @ARGV = @logfiles; while (<>) { if (/ERROR/) { print "Found in $ARGV at line $.: $_"; } }
For per-file analysis with reset:
That 'continue' block with 'eof' is another piece of arcane Perl wisdom - it detects when we've finished a file and closes it, resetting $. for the next file.local @ARGV = @logfiles; my %stats; while (<>) { $stats{$ARGV}{lines}++; $stats{$ARGV}{errors}++ if /ERROR/; } continue { # Reset line counter between files close ARGV if eof; }
Part 6: THE DARK SIDE
Before you go rewriting all your file I/O, some caveats:- ERROR HANDLING IS TRICKY
For production code, you probably want proper open() with error handling.# This won't die on missing file the way open() would my $content = do { local @ARGV = ("nonexistent.txt"); <> }; # $content is just undef, no error raised
- BINARY FILES ARE RISKY
The diamond operator does line-oriented reading. Binary files with embedded newlines will confuse it. Stick to open() with binmode().
- IT'S OBSCURE
The next person reading your code (including future you) might not immediately grok what's happening. Sometimes boring is better.
Part 7: THE HYBRID APPROACH
Want the brevity without the obscurity? Consider File::Slurp:Or in modern Perl with Path::Tiny:use File::Slurp qw(read_file); my $content = read_file($filename);
But where's the fun in that? Sometimes you want to reach into Perl's bag of tricks and pull out something delightfully weird. The @ARGV slurp is exactly that kind of trick.use Path::Tiny; my $content = path($filename)->slurp;
It won't make your code clearer. It won't make it faster. But it will make you appreciate just how malleable Perl's internals really are.
The @ARGV file slurp is the kind of technique you probably shouldn't use in production code. But understanding it? That's how you level up from "knowing Perl" to "thinking in Perl."
Sometimes the journey through the weird parts is the whole point.
perl.gg