mirror of
https://github.com/cloudstreet-dev/The-PERL-Programming-Language.git
synced 2025-10-04 11:31:32 +02:00
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 <noreply@anthropic.com>
417 lines
12 KiB
Markdown
417 lines
12 KiB
Markdown
# 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 = <DATA>; # List context: all lines
|
||
my $line = <DATA>; # 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.* |