╔╦╗╦╔═╗╔╦╗╔═╗╔╗╔╔╦╗ ║║║╠═╣║║║║ ║║║║ ║║ ═╩╝╩╩ ╩╩ ╩╚═╝╝╚╝═╩╝ ╔═╗╔═╗╔═╗╦═╗╔═╗╔╦╗╔═╗╦═╗ ║ ║╠═╝║╣ ╠╦╝╠═╣ ║ ║ ║╠╦╝ ╚═╝╩ ╚═╝╩╚═╩ ╩ ╩ ╚═╝╩╚═ ============================================================================ Two angle brackets. Empty: <> The diamond operator. Also called the null filehandle. Every Perl scripter uses it, often without knowing its name. It reads from files or STDIN, magically. ============================================================================ PART 1: THE MAGIC ----------------- while (<>) { print; } Run this script three ways: perl script.pl file1.txt file2.txt # Reads both files cat data.txt | perl script.pl # Reads from pipe perl script.pl # Reads from keyboard The diamond figures out what you meant. Files on command line? Read those. Nothing? Read STDIN. It just works. ============================================================================ PART 2: HOW IT WORKS -------------------- The diamond reads from @ARGV if it has files, otherwise from STDIN. Step by step: 1. Check @ARGV 2. If files exist, open and read the first one 3. When exhausted, move to the next file in @ARGV 4. If @ARGV is empty, read from STDIN It's the behavior that makes Unix filter programs work: grep pattern file.txt # Read from file cat file.txt | grep pattern # Read from pipe Same command, different input sources. .--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/ ============================================================================ PART 3: THE -n AND -p SWITCHES ------------------------------ The diamond is why -n and -p work: perl -ne 'print if /pattern/' file.txt This wraps your code in: while (<>) { print if /pattern/; } The diamond handles the file automatically. ============================================================================ PART 4: MULTIPLE FILES ---------------------- Process many files seamlessly: perl -ne 'print "$ARGV: $_"' *.txt $ARGV contains the current filename. The diamond reads each file in sequence. Output: file1.txt: first line of file1 file1.txt: second line of file1 file2.txt: first line of file2 ... ============================================================================ PART 5: LINE NUMBERS -------------------- $. is the line number: while (<>) { print "$.: $_"; } But here's a gotcha: $. doesn't reset between files! perl -ne 'print "$.: $_"' file1.txt file2.txt If file1.txt has 10 lines, file2.txt starts at line 11. To reset, close ARGV explicitly: while (<>) { print "$ARGV:$.: $_"; } continue { close ARGV if eof; # Reset $. for next file } ============================================================================ PART 6: MODIFYING @ARGV ----------------------- You can manipulate @ARGV before the loop: # Only process .log files @ARGV = grep { /\.log$/ } @ARGV; while (<>) { # ... } Or add files programmatically: push @ARGV, '/var/log/syslog' unless @ARGV; while (<>) { # ... } Default to syslog if no files given. ============================================================================ PART 7: THE DASH TRICK ---------------------- A single dash means STDIN: perl script.pl file1.txt - file2.txt This reads file1, then STDIN, then file2. Useful for mixing file input with piped data. echo "injected line" | perl script.pl before.txt - after.txt ============================================================================ PART 8: IN-PLACE EDITING ------------------------ The diamond enables -i (in-place editing): perl -i.bak -pe 's/old/new/g' file.txt This: 1. Reads file.txt via diamond 2. Processes each line 3. Writes back to the same file 4. Saves original as file.txt.bak The diamond is doing all the file handling. ============================================================================ PART 9: READING ALL AT ONCE --------------------------- Slurp mode reads entire file: local $/ = undef; # Unset record separator my $content = <>; # Read entire file(s) Or multiple files into an array: my @all_lines = <>; # All lines from all files Warning: memory-intensive for large files. ============================================================================ PART 10: EXPLICIT FILEHANDLES ----------------------------- Diamond reads from ARGV implicitly. For explicit control: open my $fh, '<', 'file.txt' or die; while (<$fh>) { # ... } Or the old-school bareword: open FH, '<', 'file.txt' or die; while () { # ... } Diamond is for when you want the magic. ============================================================================ PART 11: THE SECURITY PROBLEM ----------------------------- Here's the dark side. The diamond uses two-argument open: while (<>) # Internally: open(ARGV, $ARGV[0]) Two-argument open interprets special characters: perl script.pl 'rm -rf / |' This doesn't read a file called "rm -rf / |". It EXECUTES that command and reads its output. Dangerous if @ARGV comes from untrusted input. Fix: use the double diamond (next tutorial) or validate @ARGV. ============================================================================ PART 12: USEFUL PATTERNS ------------------------ Count lines in multiple files: my $total = 0; while (<>) { $total++; } print "Total lines: $total\n"; Search and report with filenames: while (<>) { print "$ARGV:$.: $_" if /pattern/; } continue { close ARGV if eof; } Concatenate files with headers: my $current = ''; while (<>) { if ($ARGV ne $current) { print "=== $ARGV ===\n"; $current = $ARGV; } print; } ============================================================================ PART 13: THE NAME ----------------- Look at it: <> It's a diamond shape. Or an empty box. Or angle brackets with nothing inside. "Null filehandle" is the technical name. "Diamond operator" is what everyone calls it. The angles point outward like rays of magic, pulling input from wherever it needs to come from. ============================================================================ <> / \ / \ < > \ / \ / \/ Magic input since Perl 1 ============================================================================ japh.codes