From 37128dedfbf6911d5b192c4452e145c51f3e71e5 Mon Sep 17 00:00:00 2001 From: David Liedle Date: Sun, 14 Sep 2025 11:41:48 -0600 Subject: [PATCH] Complete Perl Programming Language book for modern system administrators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This comprehensive guide covers: - 22 chapters of practical Perl programming - Focus on system administration and automation - Modern Perl best practices and techniques - Real-world examples and production-ready code - 3 appendices with one-liners, gotchas, and resources The book targets experienced sysadmins, DevOps engineers, and automation specialists, demonstrating Perl's continued relevance in 2025 for text processing, system administration, and rapid development. 🤖 Generated with Claude Code Co-Authored-By: Claude --- 00-table-of-contents.md | 40 + 01-why-perl-still-matters.md | 98 ++ 02-getting-started-modern-perl-setup.md | 350 +++++ ...l-fundamentals-variables-and-data-types.md | 417 ++++++ 04-control-flow-and-subroutines.md | 571 +++++++ 05-regular-expressions-perls-superpower.md | 541 +++++++ 06-file-io-and-directory-operations.md | 624 ++++++++ 07-advanced-text-processing.md | 782 ++++++++++ 08-working-with-csv-json-xml.md | 748 ++++++++++ 09-log-file-analysis-and-monitoring.md | 854 +++++++++++ 10-process-management-and-system-commands.md | 834 +++++++++++ 11-network-programming-and-web-scraping.md | 855 +++++++++++ 12-database-operations-with-dbi.md | 1067 +++++++++++++ 13-configuration-management-and-templating.md | 1047 +++++++++++++ 14-cpan-the-treasure-trove.md | 877 +++++++++++ 15-object-oriented-perl.md | 929 ++++++++++++ 16-testing-and-debugging.md | 801 ++++++++++ 17-performance-and-optimization.md | 780 ++++++++++ 18-building-command-line-tools.md | 958 ++++++++++++ 19-system-monitoring-and-alerting-scripts.md | 1227 +++++++++++++++ 20-automation-workflows-and-cron-jobs.md | 1218 +++++++++++++++ 21-restful-apis-and-web-services.md | 1325 +++++++++++++++++ 22-security-best-practices.md | 1277 ++++++++++++++++ README.md | 185 +++ appendix-a-perl-one-liners-cookbook.md | 525 +++++++ appendix-b-common-gotchas-and-solutions.md | 623 ++++++++ appendix-c-resources-and-community.md | 441 ++++++ 27 files changed, 19994 insertions(+) create mode 100644 00-table-of-contents.md create mode 100644 01-why-perl-still-matters.md create mode 100644 02-getting-started-modern-perl-setup.md create mode 100644 03-perl-fundamentals-variables-and-data-types.md create mode 100644 04-control-flow-and-subroutines.md create mode 100644 05-regular-expressions-perls-superpower.md create mode 100644 06-file-io-and-directory-operations.md create mode 100644 07-advanced-text-processing.md create mode 100644 08-working-with-csv-json-xml.md create mode 100644 09-log-file-analysis-and-monitoring.md create mode 100644 10-process-management-and-system-commands.md create mode 100644 11-network-programming-and-web-scraping.md create mode 100644 12-database-operations-with-dbi.md create mode 100644 13-configuration-management-and-templating.md create mode 100644 14-cpan-the-treasure-trove.md create mode 100644 15-object-oriented-perl.md create mode 100644 16-testing-and-debugging.md create mode 100644 17-performance-and-optimization.md create mode 100644 18-building-command-line-tools.md create mode 100644 19-system-monitoring-and-alerting-scripts.md create mode 100644 20-automation-workflows-and-cron-jobs.md create mode 100644 21-restful-apis-and-web-services.md create mode 100644 22-security-best-practices.md create mode 100644 README.md create mode 100644 appendix-a-perl-one-liners-cookbook.md create mode 100644 appendix-b-common-gotchas-and-solutions.md create mode 100644 appendix-c-resources-and-community.md diff --git a/00-table-of-contents.md b/00-table-of-contents.md new file mode 100644 index 0000000..a4e4311 --- /dev/null +++ b/00-table-of-contents.md @@ -0,0 +1,40 @@ +# The PERL Programming Language: A Modern Guide for System Administrators and Automation Engineers + +## Table of Contents + +### Part I: Foundations +- **Chapter 1**: Why Perl Still Matters in 2025 +- **Chapter 2**: Getting Started - Modern Perl Setup +- **Chapter 3**: Perl Fundamentals - Variables and Data Types +- **Chapter 4**: Control Flow and Subroutines +- **Chapter 5**: Regular Expressions - Perl's Superpower + +### Part II: Text Processing and File Operations +- **Chapter 6**: File I/O and Directory Operations +- **Chapter 7**: Advanced Text Processing +- **Chapter 8**: Working with CSV, JSON, and XML +- **Chapter 9**: Log File Analysis and Monitoring + +### Part III: System Administration +- **Chapter 10**: Process Management and System Commands +- **Chapter 11**: Network Programming and Web Scraping +- **Chapter 12**: Database Operations with DBI +- **Chapter 13**: Configuration Management and Templating + +### Part IV: Modern Perl Development +- **Chapter 14**: CPAN - The Treasure Trove +- **Chapter 15**: Object-Oriented Perl +- **Chapter 16**: Testing and Debugging +- **Chapter 17**: Performance and Optimization + +### Part V: Real-World Applications +- **Chapter 18**: Building Command-Line Tools +- **Chapter 19**: System Monitoring and Alerting Scripts +- **Chapter 20**: Automation Workflows and Cron Jobs +- **Chapter 21**: RESTful APIs and Web Services +- **Chapter 22**: Security Best Practices + +### Appendices +- **Appendix A**: Perl One-Liners Cookbook +- **Appendix B**: Common Gotchas and Solutions +- **Appendix C**: Resources and Community \ No newline at end of file diff --git a/01-why-perl-still-matters.md b/01-why-perl-still-matters.md new file mode 100644 index 0000000..9b87c1b --- /dev/null +++ b/01-why-perl-still-matters.md @@ -0,0 +1,98 @@ +# Chapter 1: Why Perl Still Matters in 2025 + +> "Perl is the duct tape of the Internet." - Hassan Schroeder + +If you're reading this in 2025, you've probably heard Perl is dead. You've been told Python ate its lunch, Ruby stole its dinner, and Go is drinking its milkshake. Yet here's the thing: millions of lines of Perl code are running right now, processing your credit card transactions, analyzing genomic data, managing content for major websites, and quietly keeping the internet's plumbing functional. + +## The Rumors of Perl's Death Have Been Greatly Exaggerated + +Let me tell you a secret that Silicon Valley's trendiest developers won't admit: Perl never left. While everyone was busy rewriting their microservices for the third time this year, Perl has been steadily processing terabytes of logs, managing critical infrastructure, and doing what it's always done best—getting stuff done without the fanfare. + +Consider this: Amazon's backend? Built on Perl. DuckDuckGo? Perl. Booking.com, serving millions of hotel reservations daily? You guessed it—Perl. The Human Genome Project? Analyzed with Perl. Your bank's overnight batch processing? There's a good chance Perl is involved. + +## Why SysAdmins Still Love Perl + +Picture this scenario: It's 3 AM, a critical log processing system has failed, and you need to parse 50GB of semi-structured logs to find out why. You could: + +1. Spin up a Spark cluster (45 minutes) +2. Write a Python script with pandas (hope you have enough RAM) +3. Use a one-line Perl command that completes in 2 minutes + +```perl +perl -ne 'print if /ERROR.*timeout/ && /user_id=(\d+)/' massive.log | sort | uniq -c +``` + +This is why seasoned sysadmins keep Perl in their toolkit. It's not about being old-school; it's about being effective. + +## The Swiss Army Chainsaw + +Larry Wall didn't design Perl to be elegant. He designed it to be useful. While other languages pride themselves on having "one obvious way" to do things, Perl celebrates choice. This philosophy—"There's More Than One Way To Do It" (TMTOWTDI, pronounced "Tim Toady")—is both Perl's greatest strength and the source of its reputation. + +Yes, you can write unreadable Perl. You can also write unreadable Python, Go, or Rust. The difference is that Perl doesn't pretend otherwise. It trusts you to be an adult. + +## What Makes Perl Special in 2025? + +### 1. **Unmatched Text Processing** +No language—not Python, not Ruby, not even modern JavaScript—comes close to Perl's text manipulation capabilities. Regular expressions aren't bolted on; they're part of the language's DNA. + +```perl +# Find all email addresses in a file and count domains +perl -ne 'print "$1\n" while /[\w.-]+@([\w.-]+)/g' emails.txt | sort | uniq -c +``` + +Try writing that as concisely in any other language. + +### 2. **CPAN: The Original Package Repository** +Before npm, before pip, before gem, there was CPAN (Comprehensive Perl Archive Network). With over 200,000 modules, if you need to do something, someone has probably already written a Perl module for it. And unlike the JavaScript ecosystem, these modules tend to be stable for decades, not deprecated every six months. + +### 3. **Backward Compatibility That Actually Works** +That Perl script you wrote in 2005? It still runs. No "Perl 2 vs Perl 3" drama. No breaking changes every major version. Perl respects your investment in code. + +### 4. **Speed Where It Counts** +For text processing, log analysis, and system automation tasks, Perl often outperforms "faster" languages. Why? Because these tasks play to Perl's strengths: regular expressions are compiled once and cached, string operations are optimized at the C level, and the interpreter is tuned for exactly these use cases. + +## The Modern Perl Renaissance + +Here's what the "Perl is dead" crowd doesn't know: Perl 7 is coming, and the language has been quietly modernizing. Recent versions have added: + +- Subroutine signatures (finally!) +- Postfix dereferencing (cleaner syntax) +- Unicode improvements +- Better error messages +- Performance enhancements + +The Perl community learned from the Python 2/3 debacle and is managing the transition carefully, maintaining backward compatibility while modernizing the language. + +## Who Should Learn Perl in 2025? + +You should learn Perl if you: + +- Manage Linux/Unix systems +- Process large amounts of text data +- Need quick, reliable automation scripts +- Work with legacy systems (they're not going anywhere) +- Appreciate tools that prioritize getting work done over looking pretty +- Want to understand how modern scripting languages evolved + +You might skip Perl if you: + +- Only build front-end web applications +- Need a language with strong typing guarantees +- Work exclusively in Windows environments (though Perl works there too) +- Prefer languages with stricter style guidelines + +## A Language for Pragmatists + +Perl isn't trying to win beauty contests. It's not the language you learn to impress people at tech meetups. It's the language you learn because you have work to do, and you want to do it efficiently. + +In 2025, we have languages optimized for every conceivable metric: execution speed (Rust), developer happiness (Ruby), simplicity (Go), type safety (Haskell). Perl is optimized for something different: getting things done. And in the real world of system administration, data munging, and keeping the lights on, that's often exactly what you need. + +## What's Next? + +In the coming chapters, we'll dive into practical Perl. You'll learn not just the syntax, but the idioms that make Perl powerful. We'll write real scripts that solve real problems—the kind you encounter at 3 AM when production is down and Stack Overflow isn't helping. + +But first, let's get your environment set up. Because the best way to learn Perl isn't to read about it—it's to write it. + +--- + +*Remember: In the world of system administration and automation, the question isn't "Is this the newest technology?" The question is "Does this solve my problem?" And for countless problems, the answer is still Perl.* \ No newline at end of file diff --git a/02-getting-started-modern-perl-setup.md b/02-getting-started-modern-perl-setup.md new file mode 100644 index 0000000..02fe651 --- /dev/null +++ b/02-getting-started-modern-perl-setup.md @@ -0,0 +1,350 @@ +# Chapter 2: Getting Started - Modern Perl Setup + +> "A good workman never blames his tools, but a smart one makes sure they're sharp." - Anonymous SysAdmin + +Before we dive into Perl's syntax and philosophy, let's get your environment set up properly. If you're going to be a Perl programmer in 2025, you might as well do it right from the start. This chapter will save you hours of frustration and teach you how the pros manage their Perl installations. + +## The Perl That's Already There (And Why You Shouldn't Use It) + +Open a terminal on almost any Unix-like system and type: + +```bash +perl -v +``` + +Congratulations! You probably have Perl installed. On macOS, Linux, BSD—it's there. This is both a blessing and a curse. + +The blessing: Perl is so useful for system tasks that OS vendors include it by default. + +The curse: That system Perl is there for the OS, not for you. It might be ancient (I'm looking at you, CentOS), it definitely has modules the system depends on, and updating it could break things in spectacular ways. + +**Golden Rule #1**: Never mess with system Perl. Leave it alone. It's not yours. + +## Enter Perlbrew: Your Personal Perl Paradise + +The solution? Install your own Perl. And the easiest way to do that is with Perlbrew—a tool that lets you install and manage multiple Perl versions without sudo, without conflicting with system Perl, and without tears. + +### Installing Perlbrew + +On macOS with Homebrew: +```bash +brew install perlbrew +``` + +On Linux/Unix: +```bash +curl -L https://install.perlbrew.pl | bash +``` + +Now add this to your shell configuration (`~/.bashrc`, `~/.zshrc`, etc.): +```bash +source ~/perl5/perlbrew/etc/bashrc +``` + +Restart your terminal or run: +```bash +exec $SHELL +``` + +### Your First Personal Perl + +Let's install a fresh, modern Perl: + +```bash +# See available versions +perlbrew available + +# Install the latest stable version (as of 2025, this would be 5.38+) +perlbrew install perl-5.38.2 + +# Or install with threading support (useful for some modules) +perlbrew install perl-5.38.2 -Dusethreads + +# This will take a few minutes. Grab coffee. Perl is compiling. +``` + +Once installed: +```bash +# List your installed Perls +perlbrew list + +# Switch to your new Perl +perlbrew switch perl-5.38.2 + +# Verify +perl -v +which perl # Should show something like /home/you/perl5/perlbrew/perls/perl-5.38.2/bin/perl +``` + +Boom! You now have your own Perl that you can upgrade, modify, and experiment with without fear. + +## CPAN: Your New Best Friend + +CPAN (Comprehensive Perl Archive Network) is Perl's killer feature. It's a massive repository of reusable code that's been solving problems since 1995. But first, we need to configure it properly. + +### First-Time CPAN Setup + +Run: +```bash +cpan +``` + +The first time you run CPAN, it'll ask if you want automatic configuration. Say yes. It's smart enough to figure out your system. + +But we can do better. Let's install `cpanminus` (cpanm), a more modern, zero-configuration CPAN client: + +```bash +# Install cpanminus +cpan App::cpanminus + +# Or, if you prefer a one-liner: +curl -L https://cpanmin.us | perl - App::cpanminus +``` + +Now you can install modules with: +```bash +cpanm Module::Name +``` + +No configuration dialogs, no interactive prompts, just installation. This is how we'll install modules throughout this book. + +### Essential Modern Perl Modules + +Let's install some modules that make Perl more pleasant in 2025: + +```bash +# Modern::Perl - Enable modern Perl features with one line +cpanm Modern::Perl + +# Perl::Tidy - Format your code consistently +cpanm Perl::Tidy + +# Perl::Critic - Lint your code for best practices +cpanm Perl::Critic + +# Data::Dumper - Inspect data structures (probably already installed) +cpanm Data::Dumper + +# Try::Tiny - Better exception handling +cpanm Try::Tiny + +# Path::Tiny - File path manipulation done right +cpanm Path::Tiny + +# JSON::XS - Fast JSON parsing +cpanm JSON::XS +``` + +## Your First Modern Perl Script + +Let's write a simple script using modern Perl features. Create a file called `hello_modern.pl`: + +```perl +#!/usr/bin/env perl +use Modern::Perl '2023'; +use feature 'signatures'; +no warnings 'experimental::signatures'; + +sub greet($name = 'World') { + say "Hello, $name!"; +} + +greet(); +greet('Perl Hacker'); + +# Let's use some modern features +my @languages = qw(Perl Python Ruby Go Rust); +say "Languages I respect: " . join(', ', @languages); + +# Postfix dereferencing (Perl 5.20+) +my $hashref = { name => 'Larry', language => 'Perl' }; +say "Creator: " . $hashref->%*{'name'}; + +# State variables (like static in C) +sub counter { + state $count = 0; + return ++$count; +} + +say "Counter: " . counter() for 1..3; +``` + +Run it: +```bash +perl hello_modern.pl +``` + +Notice what we did: +- `use Modern::Perl '2023'` enables strict, warnings, and modern features +- Subroutine signatures (no more `my ($arg1, $arg2) = @_`) +- `say` instead of `print` (automatic newline) +- State variables that maintain their value between calls +- Postfix dereferencing for cleaner syntax + +## Setting Up Your Editor + +You can write Perl in any text editor, but some setup makes life easier. Here are configurations for popular editors: + +### VS Code +Install the "Perl" extension by Gerald Richter. It provides: +- Syntax highlighting +- Code formatting via Perl::Tidy +- Linting via Perl::Critic +- Debugger support + +### Vim/Neovim +Add to your `.vimrc`: +```vim +" Perl-specific settings +autocmd FileType perl setlocal tabstop=4 shiftwidth=4 expandtab +autocmd FileType perl setlocal cindent +autocmd FileType perl setlocal cinkeys-=0# + +" Run perltidy on save (optional) +autocmd BufWritePre *.pl :%!perltidy -q +``` + +### Emacs +Emacs has excellent Perl support out of the box with `cperl-mode`: +```elisp +(defalias 'perl-mode 'cperl-mode) +(setq cperl-indent-level 4) +(setq cperl-close-paren-offset -4) +``` + +## Creating a Perl Project Structure + +Unlike some languages, Perl doesn't enforce a project structure. But here's a sensible layout for modern Perl projects: + +``` +my-perl-project/ +├── lib/ # Your modules +├── script/ # Executable scripts +├── t/ # Tests +├── cpanfile # CPAN dependencies +├── .perltidyrc # Code formatting config +├── .perlcriticrc # Linting config +└── README.md +``` + +### Managing Dependencies with cpanfile + +Create a `cpanfile` to track your project's dependencies: + +```perl +requires 'Modern::Perl', '1.20230701'; +requires 'Try::Tiny', '0.31'; +requires 'Path::Tiny', '0.144'; + +on 'test' => sub { + requires 'Test::More', '1.302195'; + requires 'Test::Exception', '0.43'; +}; +``` + +Install dependencies: +```bash +cpanm --installdeps . +``` + +### Code Formatting with Perl::Tidy + +Create `.perltidyrc`: +``` +# Indent style +--indent-columns=4 +--continuation-indentation=4 + +# Whitespace +--add-whitespace +--noblanks-before-blocks +--blanks-before-subs + +# Line length +--maximum-line-length=100 + +# Braces +--opening-brace-on-new-line +--closing-brace-else-on-same-line +``` + +Format your code: +```bash +perltidy script.pl +``` + +### Linting with Perl::Critic + +Create `.perlcriticrc`: +``` +severity = 3 +theme = core + +[TestingAndDebugging::RequireUseStrict] +severity = 5 + +[TestingAndDebugging::RequireUseWarnings] +severity = 5 +``` + +Check your code: +```bash +perlcritic script.pl +``` + +## The Perl Development Workflow + +Here's a typical workflow for developing Perl scripts in 2025: + +1. **Start with a shebang and Modern::Perl** + ```perl + #!/usr/bin/env perl + use Modern::Perl '2023'; + ``` + +2. **Write your code** + +3. **Format with perltidy** + ```bash + perltidy -b script.pl # -b backs up original + ``` + +4. **Check with perlcritic** + ```bash + perlcritic script.pl + ``` + +5. **Test thoroughly** (we'll cover testing in a later chapter) + +6. **Document as you go** (POD - Plain Old Documentation) + +## Common Gotchas and Solutions + +### Problem: "Can't locate Module/Name.pm in @INC" +**Solution**: You forgot to install the module. Run `cpanm Module::Name` + +### Problem: Script works on your machine but not on the server +**Solution**: Check Perl versions (`perl -v`) and installed modules. Use `cpanfile` to track dependencies. + +### Problem: "Perl is too old" errors +**Solution**: That's why we installed our own Perl with Perlbrew! + +### Problem: Different behavior on different systems +**Solution**: Always use `#!/usr/bin/env perl`, not `#!/usr/bin/perl`. This uses the Perl in your PATH. + +## Your Toolkit Is Ready + +You now have: +- A modern Perl installation you control +- CPAN modules at your fingertips +- An editor configured for Perl development +- Tools for formatting and linting your code +- A sensible project structure + +This is the foundation every serious Perl programmer needs. You're not writing CGI scripts in 1999; you're writing modern, maintainable Perl in 2025. + +In the next chapter, we'll dive into Perl's data types and variables. But unlike other tutorials, we'll focus on what makes Perl's approach unique and powerful for system administration and text processing tasks. + +--- + +*Pro tip: Bookmark [metacpan.org](https://metacpan.org). It's the modern web interface to CPAN with better search, documentation, and dependency information. When you need a module for something, start there.* \ No newline at end of file diff --git a/03-perl-fundamentals-variables-and-data-types.md b/03-perl-fundamentals-variables-and-data-types.md new file mode 100644 index 0000000..9f12198 --- /dev/null +++ b/03-perl-fundamentals-variables-and-data-types.md @@ -0,0 +1,417 @@ +# Chapter 3: Perl Fundamentals - Variables and Data Types + +> "In Perl, the variable tells you what it contains, not what it is." - Larry Wall + +If you're coming from other languages, Perl's approach to variables might seem... different. Where Python has lists and dicts, and C has int and char, Perl has scalars, arrays, and hashes. But here's the thing: this simplicity is deceptive. These three data types, combined with references and context, give you incredible flexibility. Let's explore. + +## The Sigils: Perl's Type System + +In Perl, variables wear their type on their sleeve—literally. The symbol at the beginning of a variable (called a sigil) tells you what kind of data it holds: + +- `$` - Scalar (single value) +- `@` - Array (ordered list) +- `%` - Hash (key-value pairs) + +This isn't just syntax; it's documentation. When you see `$name`, you know it's a single value. When you see `@names`, you know it's a list. No guessing, no IDE required. + +```perl +#!/usr/bin/env perl +use Modern::Perl '2023'; + +my $server = 'web01.prod'; # Scalar: single value +my @servers = qw(web01 web02 web03); # Array: list of values +my %status = ( # Hash: key-value pairs + web01 => 'running', + web02 => 'running', + web03 => 'maintenance' +); +``` + +## Scalars: Not Just Simple Values + +The name "scalar" suggests simplicity, but Perl scalars are surprisingly sophisticated. A scalar can hold: + +```perl +my $integer = 42; +my $float = 3.14159; +my $string = "Hello, World"; +my $huge_number = 123_456_789; # Underscores for readability +my $binary = 0b11111111; # Binary literal (255) +my $hex = 0xFF; # Hexadecimal literal (255) +my $octal = 0755; # Octal literal (493) + +# Perl converts between types automatically +my $answer = "42"; +my $result = $answer + 8; # 50 - Perl converts string to number +say "The answer is $result"; + +# But be careful with strings that don't look like numbers +my $name = "Larry"; +my $bad_math = $name + 5; # 5 - "Larry" becomes 0 in numeric context +``` + +### The Magic of Interpolation + +One of Perl's most loved features is string interpolation: + +```perl +my $user = 'alice'; +my $home = '/home'; +my $path = "$home/$user"; # Double quotes interpolate +say "User $user home: $path"; + +# But single quotes don't interpolate +my $literal = '$home/$user'; # Literally: $home/$user +say 'This is literal: $literal'; # Prints: This is literal: $literal + +# Need a literal $ in double quotes? Escape it +say "The cost is \$42.00"; + +# Complex expressions need curly braces +my $count = 5; +say "Next value: ${count}1"; # Next value: 51 +say "Squared: @{[$count**2]}"; # Squared: 25 (array interpolation trick) +``` + +### Undefined Values and Truth + +Perl has a special value `undef` that represents "no value": + +```perl +my $nothing; # $nothing is undef +my $something = undef; # Explicitly undef + +# Check for definedness +if (defined $nothing) { + say "This won't print"; +} + +# Perl's truth rules: +# False: undef, 0, "0", "" (empty string), empty list +# True: Everything else (including "00", "0.0", negative numbers) + +my @falsy_values = (undef, 0, "0", ""); +for my $val (@falsy_values) { + say "Value " . ($val // 'undef') . " is false" unless $val; +} + +# The // operator (defined-or) is incredibly useful +my $port = $ENV{PORT} // 8080; # Use env var or default to 8080 +``` + +## Arrays: Lists with Attitude + +Perl arrays are ordered, integer-indexed collections. But they're more flexible than arrays in most languages: + +```perl +# Multiple ways to create arrays +my @empty = (); +my @numbers = (1, 2, 3, 4, 5); +my @words = qw(apple banana cherry); # qw = quote words +my @mixed = (42, "hello", 3.14, undef); # Mixed types? No problem! + +# Array operations +push @numbers, 6; # Add to end +my $last = pop @numbers; # Remove and return last element +unshift @numbers, 0; # Add to beginning +my $first = shift @numbers; # Remove and return first element + +# Array access +say $numbers[0]; # First element (note the $) +say $numbers[-1]; # Last element (negative indexing!) +say $numbers[-2]; # Second to last + +# Array slices +my @subset = @numbers[1..3]; # Elements 1, 2, 3 +my @selection = @numbers[0, 2, 4]; # Elements 0, 2, 4 + +# Array size +my $size = @numbers; # Array in scalar context gives size +my $last_index = $#numbers; # Last valid index + +# Useful array operations +my @sorted = sort @words; +my @reversed = reverse @numbers; +my @unique = do { my %seen; grep { !$seen{$_}++ } @mixed }; +``` + +### The Power of List Context + +Perl's context sensitivity is unique. The same expression can return different values depending on context: + +```perl +my @lines = ; # List context: all lines +my $line = ; # Scalar context: one line + +# Many functions are context-aware +my @matches = $text =~ /\w+/g; # List context: all matches +my $count = $text =~ /\w+/g; # Scalar context: count of matches + +# Force context +my $count = @array; # Scalar context +my @copy = @{[@array]}; # List context (array ref then deref) +my ($first) = @array; # List context, but only taking first element +``` + +## Hashes: The Swiss Army Data Structure + +Hashes (associative arrays) map keys to values. They're unordered but incredibly fast for lookups: + +```perl +# Creating hashes +my %empty = (); +my %config = ( + host => 'localhost', + port => 8080, + ssl => 1, +); + +# Fat comma => is just a fancy comma that quotes the left side +my %same_config = ('host', 'localhost', 'port', 8080, 'ssl', 1); + +# Hash operations +$config{timeout} = 30; # Add/update +my $host = $config{host}; # Access (note the $) +delete $config{ssl}; # Remove key +my $exists = exists $config{port}; # Check if key exists + +# Hash slices +my @values = @config{qw(host port)}; # Get multiple values +@config{qw(user pass)} = qw(admin secret); # Set multiple values + +# Iterating hashes +for my $key (keys %config) { + say "$key: $config{$key}"; +} + +# Or with each (stateful iterator) +while (my ($key, $value) = each %config) { + say "$key => $value"; +} + +# Getting all keys and values +my @keys = keys %config; +my @values = values %config; +``` + +### Real-World Hash Example: Log Analysis + +```perl +#!/usr/bin/env perl +use Modern::Perl '2023'; + +# Count occurrences of each IP in a log +my %ip_count; +while (<>) { # Read from files or STDIN + if (/(\d+\.\d+\.\d+\.\d+)/) { + $ip_count{$1}++; + } +} + +# Sort by count and display top 10 +my @sorted = sort { $ip_count{$b} <=> $ip_count{$a} } keys %ip_count; +for my $ip (@sorted[0..9]) { + last unless defined $ip; + printf "%15s: %d hits\n", $ip, $ip_count{$ip}; +} +``` + +## References: Perl's Pointers + +References let you create complex data structures. Think of them as pointers that are actually safe to use: + +```perl +# Creating references +my @array = (1, 2, 3); +my $array_ref = \@array; # Reference to array +my $anon_array = [1, 2, 3]; # Anonymous array ref + +my %hash = (a => 1, b => 2); +my $hash_ref = \%hash; # Reference to hash +my $anon_hash = {a => 1, b => 2}; # Anonymous hash ref + +# Dereferencing +my @copy = @$array_ref; # Old style +my @copy2 = @{$array_ref}; # With braces for clarity +my @copy3 = $array_ref->@*; # Postfix dereference (modern) + +# Arrow operator for accessing elements +say $array_ref->[0]; # First element +say $hash_ref->{a}; # Value for key 'a' + +# Complex data structures +my $servers = { + production => { + web => ['web01', 'web02', 'web03'], + db => ['db01', 'db02'], + }, + staging => { + web => ['staging-web01'], + db => ['staging-db01'], + }, +}; + +# Access nested data +say $servers->{production}{web}[0]; # web01 +push @{$servers->{production}{web}}, 'web04'; # Add web04 + +# Check structure with Data::Dumper +use Data::Dumper; +print Dumper($servers); +``` + +## Type Checking and Conversion + +Perl is dynamically typed but not weakly typed. It knows what type of data you have: + +```perl +use Scalar::Util qw(looks_like_number blessed reftype); + +my $maybe_number = "42"; +if (looks_like_number($maybe_number)) { + say "It's a number!"; +} + +my $ref = [1, 2, 3]; +say "Reference type: " . ref($ref); # ARRAY + +# Checking reference types +if (ref($ref) eq 'ARRAY') { + say "It's an array reference"; +} + +# More sophisticated type checking +use Ref::Util qw(is_arrayref is_hashref is_coderef); +if (is_arrayref($ref)) { + say "Definitely an array ref"; +} +``` + +## Context: The Secret Sauce + +Context is Perl's superpower. Every operation happens in either scalar or list context: + +```perl +# The same function can return different things +sub context_aware { + my @data = (1, 2, 3, 4, 5); + return wantarray ? @data : scalar(@data); +} + +my @list = context_aware(); # (1, 2, 3, 4, 5) +my $scalar = context_aware(); # 5 + +# Forcing context +my $count = () = $string =~ /pattern/g; # Force list context, get count + +# Context in file operations +my @lines = <$fh>; # Read all lines +my $line = <$fh>; # Read one line + +# The grep operator is context-aware +my @matches = grep { $_ > 5 } @numbers; # List of matches +my $count = grep { $_ > 5 } @numbers; # Count of matches +``` + +## Special Variables: Perl's Magic + +Perl has numerous special variables that make common tasks easier: + +```perl +# $_ - The default variable +for (1..5) { + say; # Prints $_ by default +} + +# @_ - Subroutine arguments +sub greet { + my ($name) = @_; # Common idiom + say "Hello, $name!"; +} + +# $! - System error message +open my $fh, '<', 'nonexistent.txt' or die "Can't open: $!"; + +# $? - Child process exit status +system('ls'); +say "Command failed: $?" if $?; + +# $$ - Process ID +say "My PID is $$"; + +# $0 - Program name +say "Running $0"; + +# @ARGV - Command line arguments +say "Arguments: @ARGV"; + +# %ENV - Environment variables +say "Home directory: $ENV{HOME}"; +``` + +## Practical Example: Server Status Monitor + +Let's put it all together with a practical script: + +```perl +#!/usr/bin/env perl +use Modern::Perl '2023'; +use Time::Piece; + +# Configuration +my %servers = ( + 'web01.prod' => { ip => '10.0.1.10', service => 'nginx' }, + 'web02.prod' => { ip => '10.0.1.11', service => 'nginx' }, + 'db01.prod' => { ip => '10.0.2.10', service => 'mysql' }, + 'cache01.prod' => { ip => '10.0.3.10', service => 'redis' }, +); + +# Check each server +my @down_servers; +my $check_time = localtime; + +for my $hostname (sort keys %servers) { + my $server = $servers{$hostname}; + my $result = `ping -c 1 -W 1 $server->{ip} 2>&1`; + + if ($? != 0) { + push @down_servers, $hostname; + $server->{status} = 'down'; + $server->{last_seen} = 'unknown'; + } else { + $server->{status} = 'up'; + $server->{last_seen} = $check_time->strftime('%Y-%m-%d %H:%M:%S'); + } +} + +# Report +say "=" x 50; +say "Server Status Report - $check_time"; +say "=" x 50; + +for my $hostname (sort keys %servers) { + my $s = $servers{$hostname}; + my $status_emoji = $s->{status} eq 'up' ? '✓' : '✗'; + printf "%-15s %-15s %-10s %s\n", + $hostname, $s->{ip}, $s->{service}, $status_emoji; +} + +if (@down_servers) { + say "\n⚠️ Alert: " . scalar(@down_servers) . " servers down!"; + say " - $_" for @down_servers; +} +``` + +## Key Takeaways + +1. **Sigils are your friends**: They make code self-documenting +2. **Context matters**: Understanding scalar vs list context is crucial +3. **References enable complexity**: But start simple +4. **Interpolation saves time**: But know when to use single quotes +5. **Special variables are powerful**: But document their use + +In the next chapter, we'll explore control flow and subroutines, where Perl's philosophy of "there's more than one way to do it" really shines. You'll learn about Perl's unique statement modifiers, the various loop constructs, and how to write subroutines that are both powerful and maintainable. + +--- + +*Remember: In Perl, the data structure you choose shapes how you think about the problem. Choose wisely, but don't overthink it. You can always refactor later.* \ No newline at end of file diff --git a/04-control-flow-and-subroutines.md b/04-control-flow-and-subroutines.md new file mode 100644 index 0000000..bfc4f94 --- /dev/null +++ b/04-control-flow-and-subroutines.md @@ -0,0 +1,571 @@ +# Chapter 4: Control Flow and Subroutines + +> "Do what I mean, not what I say." - The Perl Philosophy + +Control flow in Perl is where the language's personality really shines. Yes, you have your standard if/else and loops. But Perl adds statement modifiers, multiple ways to loop, and some of the most flexible subroutine handling you'll find anywhere. This chapter will show you how to write Perl that reads like English while being devastatingly effective. + +## Statement Modifiers: Perl's Poetry + +Most languages make you write: +```perl +if ($condition) { + do_something(); +} +``` + +Perl lets you write: +```perl +do_something() if $condition; +``` + +This isn't just syntactic sugar—it's a different way of thinking about code: + +```perl +#!/usr/bin/env perl +use Modern::Perl '2023'; + +# Traditional vs Perl style +my $file = 'config.txt'; + +# Traditional +if (-e $file) { + say "File exists"; +} + +# Perl style - when the action is more important than the condition +say "File exists" if -e $file; + +# Works with all control structures +die "Config not found!" unless -e $file; +say $_ while ; +$count++ for @items; +warn "Retrying..." until connect_to_server(); + +# Especially elegant for guard clauses +sub process_file { + my ($filename) = @_; + return unless defined $filename; + return unless -e $filename; + return unless -r $filename; + + # Main logic here, uncluttered by nested ifs +} +``` + +## The Many Faces of Conditionals + +### if, elsif, else, unless + +Perl's `unless` is the logical opposite of `if`. Use it when the negative condition is more natural: + +```perl +# Awkward double negative +if (!$user->is_authenticated) { + redirect_to_login(); +} + +# Clear and direct +unless ($user->is_authenticated) { + redirect_to_login(); +} + +# Even clearer as a modifier +redirect_to_login() unless $user->is_authenticated; + +# Complex conditionals +my $status = get_server_status(); +if ($status eq 'running') { + say "All systems operational"; +} elsif ($status eq 'degraded') { + warn "Performance degraded"; + notify_ops_team(); +} elsif ($status eq 'maintenance') { + say "Scheduled maintenance in progress"; +} else { + die "Unknown status: $status"; +} +``` + +### The Ternary Operator + +For simple conditionals, the ternary operator keeps things concise: + +```perl +my $message = $count > 0 ? "$count items found" : "No items found"; + +# Nested ternaries (use sparingly!) +my $status = $code == 200 ? 'success' : + $code == 404 ? 'not found' : + $code == 500 ? 'server error' : 'unknown'; + +# Often clearer as a dispatch table +my %status_messages = ( + 200 => 'success', + 404 => 'not found', + 500 => 'server error', +); +my $status = $status_messages{$code} // 'unknown'; +``` + +### given/when (Experimental but Useful) + +Perl's switch statement is more powerful than most: + +```perl +use feature 'switch'; +no warnings 'experimental::smartmatch'; + +given ($command) { + when ('start') { start_service() } + when ('stop') { stop_service() } + when ('restart') { stop_service(); start_service() } + when (/^stat/) { show_status() } # Regex matching! + when ([qw(help ? h)]) { show_help() } # Array matching! + default { die "Unknown command: $command" } +} +``` + +## Loops: There's More Than One Way To Iterate + +### The C-Style for Loop + +Traditional but verbose: + +```perl +for (my $i = 0; $i < 10; $i++) { + say "Count: $i"; +} +``` + +### The Perl-Style foreach + +Much more common and readable: + +```perl +# foreach and for are synonyms +for my $item (@items) { + process($item); +} + +# Default variable $_ +for (@items) { + process($_); # Or just: process() +} + +# Range operator +for my $num (1..100) { + say $num if $num % 15 == 0; # FizzBuzz anyone? +} + +# Hash iteration +for my $key (keys %hash) { + say "$key: $hash{$key}"; +} + +# Multiple items at once +my @pairs = qw(a 1 b 2 c 3); +for (my $i = 0; $i < @pairs; $i += 2) { + my ($letter, $number) = @pairs[$i, $i+1]; + say "$letter = $number"; +} +``` + +### while and until + +Perfect for conditional iteration: + +```perl +# Read file line by line +while (my $line = <$fh>) { + chomp $line; + process($line); +} + +# With default variable +while (<$fh>) { + chomp; # Operates on $_ + process($_); +} + +# until is while's opposite +my $attempts = 0; +until (connect_to_database() || $attempts++ > 5) { + sleep 2 ** $attempts; # Exponential backoff +} +``` + +### Loop Control + +Perl provides fine-grained loop control: + +```perl +# next - skip to next iteration +for my $file (@files) { + next if $file =~ /^\./; # Skip hidden files + process($file); +} + +# last - exit loop +for my $line (<$fh>) { + last if $line =~ /^__END__$/; + process($line); +} + +# redo - restart current iteration +my $tries = 0; +for my $url (@urls) { + my $response = fetch($url); + if (!$response && $tries++ < 3) { + sleep 1; + redo; # Try same URL again + } + $tries = 0; # Reset for next URL +} + +# Labels for nested loops +FILE: for my $file (@files) { + LINE: while (my $line = <$file>) { + next FILE if $line =~ /SKIP_FILE/; + next LINE if $line =~ /^\s*#/; + process($line); + } +} +``` + +### The Elegant map and grep + +Functional programming in Perl: + +```perl +# Transform lists with map +my @files = qw(foo.txt bar.log baz.conf); +my @sizes = map { -s $_ } @files; # Get file sizes +my @upper = map { uc } @words; # Uppercase all + +# Filter lists with grep +my @configs = grep { /\.conf$/ } @files; +my @large_files = grep { -s $_ > 1024 * 1024 } @files; + +# Chain them +my @large_logs = grep { -s $_ > 1024 * 1024 } + map { "$logdir/$_" } + grep { /\.log$/ } + readdir($dh); + +# Schwartzian Transform (sorting optimization) +my @sorted = map { $_->[0] } + sort { $a->[1] <=> $b->[1] } + map { [$_, -s $_] } + @files; +``` + +## Subroutines: Functions with Flexibility + +### Basic Subroutines + +```perl +# Old style (still common) +sub greet { + my ($name) = @_; # Unpack @_ + $name //= 'World'; + say "Hello, $name!"; +} + +# Modern style with signatures (Perl 5.20+) +use feature 'signatures'; +no warnings 'experimental::signatures'; + +sub greet_modern($name = 'World') { + say "Hello, $name!"; +} + +# Multiple parameters +sub calculate($x, $y, $operation = '+') { + return $x + $y if $operation eq '+'; + return $x - $y if $operation eq '-'; + return $x * $y if $operation eq '*'; + return $x / $y if $operation eq '/'; + die "Unknown operation: $operation"; +} +``` + +### Return Values and Context + +Subroutines can be context-aware: + +```perl +sub get_data { + my @data = qw(apple banana cherry); + + # wantarray tells us the context + if (wantarray) { + return @data; # List context + } elsif (defined wantarray) { + return scalar @data; # Scalar context + } else { + say "@data"; # Void context + return; + } +} + +my @fruits = get_data(); # ('apple', 'banana', 'cherry') +my $count = get_data(); # 3 +get_data(); # Prints: apple banana cherry +``` + +### Anonymous Subroutines and Closures + +Perl supports first-class functions: + +```perl +# Anonymous subroutine +my $greeter = sub { + my ($name) = @_; + say "Hello, $name!"; +}; +$greeter->('Perl'); + +# Closures capture variables +sub make_counter { + my $count = 0; + return sub { ++$count }; +} + +my $counter1 = make_counter(); +my $counter2 = make_counter(); +say $counter1->(); # 1 +say $counter1->(); # 2 +say $counter2->(); # 1 (independent counter) + +# Higher-order functions +sub apply_to_list { + my ($func, @list) = @_; + return map { $func->($_) } @list; +} + +my @doubled = apply_to_list(sub { $_ * 2 }, 1..5); +``` + +### Prototypes (Use with Caution) + +Prototypes let you create subroutines that parse like built-ins: + +```perl +# Prototype forces scalar context on first arg +sub my_push(\@@) { + my ($array_ref, @values) = @_; + push @$array_ref, @values; +} + +my @stack; +my_push @stack, 1, 2, 3; # No need for \@stack + +# Block prototype for DSL-like syntax +sub with_file(&$) { + my ($code, $filename) = @_; + open my $fh, '<', $filename or die $!; + $code->($fh); + close $fh; +} + +with_file { + my $fh = shift; + while (<$fh>) { + print if /ERROR/; + } +} 'logfile.txt'; +``` + +## Error Handling: Die, Warn, and Eval + +### Basic Error Handling + +```perl +# die - throw an exception +open my $fh, '<', $file or die "Can't open $file: $!"; + +# warn - print warning but continue +warn "Config file not found, using defaults\n" unless -e $config; + +# Custom die handler +$SIG{__DIE__} = sub { + my $message = shift; + log_error($message); + die $message; # Re-throw +}; +``` + +### Exception Handling with eval + +```perl +# Basic eval +eval { + risky_operation(); + another_risky_operation(); +}; +if ($@) { + warn "Operation failed: $@"; + # Handle error +} + +# String eval (compile and run code at runtime) +my $code = 'print "Hello, World!\n"'; +eval $code; +die "Code compilation failed: $@" if $@; +``` + +### Modern Exception Handling with Try::Tiny + +```perl +use Try::Tiny; + +try { + risky_operation(); +} catch { + warn "Caught error: $_"; + # $_ contains the error +} finally { + cleanup(); # Always runs +}; +``` + +## Practical Example: Log File Processor + +Let's combine everything into a real-world script: + +```perl +#!/usr/bin/env perl +use Modern::Perl '2023'; +use feature 'signatures'; +no warnings 'experimental::signatures'; +use Try::Tiny; +use Time::Piece; + +# Configuration +my %severity_levels = ( + DEBUG => 0, + INFO => 1, + WARNING => 2, + ERROR => 3, + FATAL => 4, +); + +# Process command line +my ($logfile, $min_level) = @ARGV; +die "Usage: $0 [min_level]\n" unless $logfile; +$min_level //= 'INFO'; + +# Main processing +process_log($logfile, $min_level); + +sub process_log($file, $min_level) { + my $min_severity = $severity_levels{$min_level} + // die "Unknown level: $min_level"; + + open my $fh, '<', $file or die "Can't open $file: $!"; + + my %stats; + my $line_count = 0; + + LINE: while (my $line = <$fh>) { + $line_count++; + chomp $line; + + # Skip empty lines and comments + next LINE if $line =~ /^\s*$/; + next LINE if $line =~ /^\s*#/; + + # Parse log line + my ($timestamp, $level, $message) = parse_line($line); + next LINE unless $timestamp; # Skip unparseable lines + + # Filter by severity + my $severity = $severity_levels{$level} // 0; + next LINE if $severity < $min_severity; + + # Collect statistics + $stats{$level}++; + + # Special handling for errors + if ($level eq 'ERROR' || $level eq 'FATAL') { + handle_error($timestamp, $level, $message); + } + } + + close $fh; + + # Report statistics + report_stats(\%stats, $line_count); +} + +sub parse_line($line) { + # Example format: 2024-01-15 10:30:45 [ERROR] Connection timeout + return unless $line =~ / + ^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+ # timestamp + \[(\w+)\]\s+ # level + (.+)$ # message + /x; + + return ($1, $2, $3); +} + +sub handle_error($timestamp, $level, $message) { + state %recent_errors; + state $last_cleanup = time; + + # Clean old errors every minute + if (time - $last_cleanup > 60) { + %recent_errors = (); + $last_cleanup = time; + } + + # Detect repeated errors + my $key = "$level:$message"; + $recent_errors{$key}++; + + if ($recent_errors{$key} == 1) { + say "[$timestamp] $level: $message"; + } elsif ($recent_errors{$key} == 10) { + warn "Error '$message' has occurred 10 times!"; + } +} + +sub report_stats($stats, $total) { + say "\n" . "=" x 40; + say "Log Analysis Summary"; + say "=" x 40; + say "Total lines processed: $total"; + say "\nEvents by severity:"; + + for my $level (sort { $severity_levels{$b} <=> $severity_levels{$a} } + keys %severity_levels) { + my $count = $stats->{$level} // 0; + next unless $count; + printf " %-8s: %6d\n", $level, $count; + } +} + +__DATA__ +2024-01-15 10:30:45 [INFO] Server started +2024-01-15 10:30:46 [DEBUG] Loading configuration +2024-01-15 10:31:00 [ERROR] Database connection failed +2024-01-15 10:31:01 [ERROR] Database connection failed +2024-01-15 10:31:02 [WARNING] Retrying database connection +``` + +## Best Practices + +1. **Use statement modifiers for simple conditions** - They make code more readable +2. **Prefer foreach over C-style for** - Unless you specifically need the index +3. **Use map and grep for transformations** - They're faster and clearer than loops +4. **Always unpack @_ at the start of subroutines** - Makes the interface clear +5. **Use state variables instead of file-scoped variables** - Better encapsulation +6. **Handle errors early and explicitly** - Don't let them propagate silently + +## Coming Up Next + +Now that you understand Perl's control flow and subroutines, you're ready for the main event: regular expressions. In the next chapter, we'll explore why Perl's regex implementation is still the gold standard, and how to wield this power effectively in your system administration tasks. + +--- + +*Remember: Good Perl code tells a story. The statement modifiers, flexible loops, and rich error handling aren't just features—they're tools for expressing your intent clearly. Use them to write code that reads like documentation.* \ No newline at end of file diff --git a/05-regular-expressions-perls-superpower.md b/05-regular-expressions-perls-superpower.md new file mode 100644 index 0000000..c0ef660 --- /dev/null +++ b/05-regular-expressions-perls-superpower.md @@ -0,0 +1,541 @@ +# Chapter 5: Regular Expressions - Perl's Superpower + +> "Some people, when confronted with a problem, think 'I know, I'll use regular expressions.' Now they have two problems." - Jamie Zawinski + +> "Those people weren't using Perl." - A Perl Programmer + +Regular expressions aren't bolted onto Perl as an afterthought or imported from a library. They're woven into the language's DNA. When other languages were struggling with clunky regex APIs, Perl developers were parsing complex log files with one-liners. This chapter will show you why Perl's regex implementation is still unmatched and how to wield this power responsibly. + +## The Basics (But Better) + +### Match Operator: m// + +```perl +#!/usr/bin/env perl +use Modern::Perl '2023'; + +my $text = "The server at 192.168.1.100 responded in 245ms"; + +# Basic matching +if ($text =~ /server/) { + say "Found 'server'"; +} + +# Capture groups +if ($text =~ /(\d+\.\d+\.\d+\.\d+)/) { + say "IP address: $1"; # $1 contains first capture +} + +# Multiple captures +if ($text =~ /at ([\d.]+) responded in (\d+)ms/) { + my ($ip, $time) = ($1, $2); + say "Server $ip took ${time}ms"; +} + +# The !~ operator for negation +say "No errors!" if $text !~ /error|fail|timeout/i; + +# Default variable $_ +$_ = "Testing 123"; +say "Contains number" if /\d+/; # No need for $_ =~ +``` + +### Substitution Operator: s/// + +```perl +my $config = "ServerName = localhost:8080"; + +# Basic substitution +$config =~ s/localhost/127.0.0.1/; +say $config; # ServerName = 127.0.0.1:8080 + +# Global substitution with /g +my $log = "Error Error Warning Error"; +$log =~ s/Error/Issue/g; +say $log; # Issue Issue Warning Issue + +# Capture and replace +my $date = "2024-01-15"; +$date =~ s/(\d{4})-(\d{2})-(\d{2})/$3\/$2\/$1/; +say $date; # 15/01/2024 + +# Using the result +my $count = $log =~ s/Warning/ALERT/g; # Returns number of replacements +say "Replaced $count warnings"; + +# The /r modifier returns modified string without changing original +my $original = "hello world"; +my $modified = $original =~ s/world/Perl/r; +say $original; # hello world (unchanged) +say $modified; # hello Perl +``` + +### The Transliteration Operator: tr/// (or y///) + +Not technically a regex, but often used alongside them: + +```perl +my $text = "Hello World 123"; + +# Count characters +my $digit_count = $text =~ tr/0-9//; +say "Contains $digit_count digits"; + +# ROT13 cipher +$text =~ tr/A-Za-z/N-ZA-Mn-za-m/; +say $text; # Uryyb Jbeyq 123 + +# Remove duplicates +$text =~ tr/a-z//s; # /s squashes duplicate characters + +# Delete characters +$text =~ tr/0-9//d; # /d deletes matched characters +``` + +## Regex Modifiers: Changing the Rules + +```perl +# /i - Case insensitive +say "Match!" if "HELLO" =~ /hello/i; + +# /x - Extended formatting (ignore whitespace, allow comments) +my $ip_regex = qr/ + ^ # Start of string + (\d{1,3}) # First octet + \. # Literal dot + (\d{1,3}) # Second octet + \. # Literal dot + (\d{1,3}) # Third octet + \. # Literal dot + (\d{1,3}) # Fourth octet + $ # End of string +/x; + +# /s - Single line mode (. matches newline) +my $html = "
\nContent\n
"; +$html =~ /
(.*?)<\/div>/s; # Captures across newlines + +# /m - Multi-line mode (^ and $ match line boundaries) +my $multi = "Line 1\nLine 2\nLine 3"; +my @lines = $multi =~ /^Line \d+$/gm; + +# /g - Global matching +my $data = "cat bat rat"; +my @words = $data =~ /\w+/g; # ('cat', 'bat', 'rat') + +# /o - Compile pattern once (optimization for loops) +for my $line (@huge_file) { + $line =~ /$pattern/o; # Pattern compiled only once +} +``` + +## Advanced Pattern Matching + +### Non-Capturing Groups + +```perl +# (?:...) doesn't create a capture variable +my $url = "https://www.example.com:8080/path"; + +if ($url =~ /^(https?):\/\/(?:www\.)?([^:\/]+)(?::(\d+))?/) { + my ($protocol, $domain, $port) = ($1, $2, $3); + $port //= $protocol eq 'https' ? 443 : 80; + say "Protocol: $protocol, Domain: $domain, Port: $port"; +} +``` + +### Named Captures (Perl 5.10+) + +```perl +# Much more readable than $1, $2, $3... +my $log_line = '2024-01-15 10:30:45 [ERROR] Connection timeout'; + +if ($log_line =~ / + (?\d{4}-\d{2}-\d{2})\s+ + (?