Ruby Regex: Everything Perl Taught You, Plus Named Captures That Actually Work
Ruby's regex engine is comparable to Perl's. Most patterns work identically. The main difference is that substitution uses methods (gsub, sub) instead of operators (s///). But Ruby has one trick that'll make Perl jealous: named captures that become real local variables. Yeah.
Part 1: Basic Matching
# Match operator (same as Perl) if line =~ %r~pattern~ puts "matched!" end # Negated match (same as Perl) if line !~ %r~pattern~ puts "no match" end # With flags line =~ %r~error~i # case-insensitive line =~ %r~^start~m # multiline (^ matches line starts) line =~ %r~complex~x # extended (ignore whitespace, allow comments)
Part 2: Substitution with gsub and sub
# Perl: $line =~ s/old/new/; # first occurrence $line =~ s/old/new/g; # all occurrences
Without the# Ruby: line.sub(%r~old~, "new") # first occurrence - returns NEW string line.gsub(%r~old~, "new") # all occurrences - returns NEW string # To modify in place (like Perl's default): line.sub!(%r~old~, "new") # modifies line directly line.gsub!(%r~old~, "new") # modifies line directly
!, the original string is NOT changed. This is the number one gotcha for Perl users. You will forget. You will stare at unchanged output for ten minutes. You will swear. Then you'll add the ! and move on.
Part 3: Extraction
# Extract first match line[%r~\d+~] # returns first number found, or nil # Extract capture group line[%r~(\d+)~, 1] # returns first capture group # Extract all matches line.scan(%r~\d+~) # returns array of ALL matches # Extract all capture groups line.scan(%r~(\w+)=(\w+)~) # returns array of [key, value] pairs
Part 4: Capture Groups
Named captures becoming local variables is a Ruby feature Perl doesn't have. In Perl you'd access them as# Numbered captures ($1, $2, etc.) - same as Perl if line =~ %r~(\d{4})-(\d{2})-(\d{2})~ year = $1 month = $2 day = $3 end # Named captures become LOCAL VARIABLES (Ruby magic!) if line =~ %r~(?<ip>\d+\.\d+\.\d+\.\d+).*(?<status>\d{3})~ puts "#{ip} returned #{status}" # ip and status are real local variables now! end
$+{ip}. In Ruby they just appear as variables. It's borderline sorcery.
Part 5: Regexp.union
Combine multiple patterns into one with automatic escaping:Perl equivalent would be manually joining with# Combine regex patterns pattern = Regexp.union(%r~Ruby\d~, %r~test~i, "literal.text") # => /(Ruby\d|[tT][eE][sS][tT]|literal\.text)/ # Strings are auto-escaped! Regexp.union("a.b", "c*d") # => /a\.b|c\*d/ # From an array of keywords keywords = %w~error warning critical~ pattern = Regexp.union(keywords) # => /error|warning|critical/ # Use it lines.select { |l| l =~ pattern }
| and worrying about escaping: my $pattern = join('|', map { quotemeta } @keywords);. Ruby just does it.
Part 6: The match Method
# Returns MatchData object (more info than $~) m = "hello 123 world".match(%r~(\w+) (\d+)~) m[0] # => "hello 123" (full match) m[1] # => "hello" (first capture) m[2] # => "123" (second capture) m.pre_match # => "" (before match) m.post_match # => " world" (after match) # With named captures m = "2024-01-15".match(%r~(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})~) m[:year] # => "2024" m[:month] # => "01" m[:day] # => "15"
Part 7: The Flag Gotcha
| Flag | Meaning | Same as Perl? |
|---|---|---|
i |
Case-insensitive | Yes |
m |
Multiline (. matches newline) |
Like Perl's s flag! |
x |
Extended (comments/whitespace) | Yes |
m flag is Perl's s flag. In Perl, m makes ^/$ match line boundaries. In Ruby, ^/$ ALWAYS match line boundaries. This will confuse you exactly once.
Part 8: gsub with Block
# Transform matches with a block line.gsub(%r~\b\w+\b~) { |match| match.capitalize } # Perl equivalent: # $line =~ s/\b\w+\b/\u\L$&/ge;
Part 9: Practical One-Liners
# Replace all IPs with REDACTED ruby -pe '$_.gsub!(%r~\d+\.\d+\.\d+\.\d+~, "REDACTED")' access.log # Extract all email addresses ruby -ne '$_.scan(%r~[\w.+-]+@[\w.-]+~).each { |e| puts e }' file.txt # Swap first two CSV columns ruby -pe '$_.sub!(%r~^([^,]+),([^,]+)~, "\\2,\\1")' data.csv
Created By: Wildcard Wizard. Copyright 2026