|
| 1 | +#!/usr/bin/env perl |
| 2 | + |
| 3 | +use strict; |
| 4 | +use warnings; |
| 5 | +use Getopt::Long qw(:config no_ignore_case); |
| 6 | +use Pod::Usage; |
| 7 | +use Data::Random::String::Matches; |
| 8 | + |
| 9 | +our $VERSION = '0.01'; |
| 10 | + |
| 11 | +# Parse command line options |
| 12 | +my %opts = ( |
| 13 | + count => 1, |
| 14 | + length => undef, |
| 15 | + smart => 0, |
| 16 | + separator => "\n", |
| 17 | + help => 0, |
| 18 | + man => 0, |
| 19 | + version => 0, |
| 20 | + examples => 0, |
| 21 | +); |
| 22 | + |
| 23 | +GetOptions( |
| 24 | + 'count|c=i' => \$opts{count}, |
| 25 | + 'length|l=i' => \$opts{length}, |
| 26 | + 'smart|s' => \$opts{smart}, |
| 27 | + 'separator|S=s' => \$opts{separator}, |
| 28 | + 'help|h|?' => \$opts{help}, |
| 29 | + 'man|m' => \$opts{man}, |
| 30 | + 'version|v' => \$opts{version}, |
| 31 | + 'examples|e' => \$opts{examples}, |
| 32 | +) or pod2usage(2); |
| 33 | + |
| 34 | +# Handle special options |
| 35 | +if ($opts{version}) { |
| 36 | + print "random-string version $VERSION\n"; |
| 37 | + exit 0; |
| 38 | +} |
| 39 | + |
| 40 | +pod2usage(1) if $opts{help}; |
| 41 | +pod2usage(-exitval => 0, -verbose => 2) if $opts{man}; |
| 42 | + |
| 43 | +if ($opts{examples}) { |
| 44 | + show_examples(); |
| 45 | + exit 0; |
| 46 | +} |
| 47 | + |
| 48 | +# Get the pattern from command line |
| 49 | +my $pattern = shift @ARGV; |
| 50 | + |
| 51 | +unless (defined $pattern) { |
| 52 | + print STDERR "Error: Pattern required\n\n"; |
| 53 | + pod2usage(1); |
| 54 | +} |
| 55 | + |
| 56 | +# Validate options |
| 57 | +if ($opts{count} < 1) { |
| 58 | + die 'Error: count must be at least 1'; |
| 59 | +} |
| 60 | + |
| 61 | +# Decode escape sequences in separator |
| 62 | +$opts{separator} =~ s/\\n/\n/g; |
| 63 | +$opts{separator} =~ s/\\t/\t/g; |
| 64 | +$opts{separator} =~ s/\\r/\r/g; |
| 65 | +$opts{separator} =~ s/\\0/\0/g; |
| 66 | + |
| 67 | +# Create generator |
| 68 | +my $gen = eval { |
| 69 | + Data::Random::String::Matches->new($pattern, $opts{length}); |
| 70 | +}; |
| 71 | + |
| 72 | +if ($@) { |
| 73 | + die "Error creating generator: $@"; |
| 74 | +} |
| 75 | + |
| 76 | +# Generate strings |
| 77 | +my @results; |
| 78 | +for (1 .. $opts{count}) { |
| 79 | + my $str = eval { |
| 80 | + $opts{smart} ? $gen->generate_smart() : $gen->generate(); |
| 81 | + }; |
| 82 | + |
| 83 | + if ($@) { |
| 84 | + die "Error generating string: $@"; |
| 85 | + } |
| 86 | + |
| 87 | + push @results, $str; |
| 88 | +} |
| 89 | + |
| 90 | +# Output results |
| 91 | +print join($opts{separator}, @results); |
| 92 | +print "\n" unless $opts{separator} =~ /\n$/; |
| 93 | + |
| 94 | +exit 0; |
| 95 | + |
| 96 | +sub show_examples { |
| 97 | + print <<'EXAMPLES'; |
| 98 | +Examples: |
| 99 | +
|
| 100 | + # Generate a single 4-digit number |
| 101 | + random-string '\d{4}' |
| 102 | +
|
| 103 | + # Generate 10 API keys |
| 104 | + random-string 'AIza[0-9A-Za-z_-]{35}' --count 10 |
| 105 | +
|
| 106 | + # Generate 5 email addresses |
| 107 | + random-string '[a-z]{5,10}@[a-z]{5,10}\.com' -c 5 |
| 108 | +
|
| 109 | + # Generate phone numbers with custom separator |
| 110 | + random-string '\d{3}-\d{3}-\d{4}' -c 3 -S ', ' |
| 111 | +
|
| 112 | + # Generate uppercase letters (3 characters) |
| 113 | + random-string '[A-Z]{3}' |
| 114 | +
|
| 115 | + # Generate password-like strings |
| 116 | + random-string '[A-Za-z0-9!@#$%]{16}' -c 5 |
| 117 | +
|
| 118 | + # Generate UUID-like strings |
| 119 | + random-string '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}' |
| 120 | +
|
| 121 | + # Use alternation |
| 122 | + random-string '(cat|dog|bird)' -c 10 |
| 123 | +
|
| 124 | + # Use backreferences (repeating pattern) |
| 125 | + random-string '(\w{3})-\1' -c 5 |
| 126 | +
|
| 127 | + # Generate credit card test numbers |
| 128 | + random-string '4\d{15}' -c 3 |
| 129 | +
|
| 130 | + # Use smart mode for complex patterns (faster) |
| 131 | + random-string '[A-Z]{3}\d{4}' -c 100 --smart |
| 132 | +
|
| 133 | +Common patterns: |
| 134 | + \d{4} - 4-digit PIN |
| 135 | + [A-Z]{2}\d{3} - License plate style |
| 136 | + \d{3}-\d{3}-\d{4} - Phone number |
| 137 | + [a-z]+@[a-z]+\.com - Simple email |
| 138 | + [A-Fa-f0-9]{32} - MD5 hash |
| 139 | + [A-Za-z0-9]{20} - Random token |
| 140 | + (foo|bar|baz) - One of three options |
| 141 | + (\w{4})-\1 - Repeated pattern |
| 142 | +
|
| 143 | +EXAMPLES |
| 144 | +} |
| 145 | + |
| 146 | +__END__ |
| 147 | +
|
| 148 | +=head1 NAME |
| 149 | +
|
| 150 | +random-string - Generate random strings matching a regular expression |
| 151 | +
|
| 152 | +=head1 SYNOPSIS |
| 153 | +
|
| 154 | +random-string [options] PATTERN |
| 155 | +
|
| 156 | + Options: |
| 157 | + -c, --count N Generate N strings (default: 1) |
| 158 | + -l, --length N Set length for fallback generation (default: 10) |
| 159 | + -s, --smart Use smart generation only (faster) |
| 160 | + -S, --separator STR Separator between outputs (default: newline) |
| 161 | + -e, --examples Show usage examples |
| 162 | + -h, --help Show brief help |
| 163 | + -m, --man Show full manual |
| 164 | + -v, --version Show version |
| 165 | +
|
| 166 | +=head1 DESCRIPTION |
| 167 | +
|
| 168 | +B<random-string> generates random strings that match a given regular expression |
| 169 | +pattern. It supports most common regex features including character classes, |
| 170 | +quantifiers, groups, alternation, and backreferences. |
| 171 | +
|
| 172 | +The pattern should be a valid Perl regular expression. The tool will generate |
| 173 | +random strings that match the pattern. |
| 174 | +
|
| 175 | +=head1 OPTIONS |
| 176 | +
|
| 177 | +=over 4 |
| 178 | +
|
| 179 | +=item B<-c, --count> N |
| 180 | +
|
| 181 | +Generate N random strings. Default is 1. |
| 182 | +
|
| 183 | +=item B<-l, --length> N |
| 184 | +
|
| 185 | +Set the length for fallback random generation. When the smart parser can't |
| 186 | +handle a pattern, it falls back to generating random strings of this length |
| 187 | +until one matches. Default is 10. |
| 188 | +
|
| 189 | +=item B<-s, --smart> |
| 190 | +
|
| 191 | +Use only the smart generation method. This parses the regex and builds matching |
| 192 | +strings directly, which is much faster for complex patterns. However, it may |
| 193 | +not handle all edge cases. |
| 194 | +
|
| 195 | +=item B<-S, --separator> STRING |
| 196 | +
|
| 197 | +String to use between multiple outputs. Default is newline. Supports escape |
| 198 | +sequences: \n (newline), \t (tab), \r (carriage return). |
| 199 | +
|
| 200 | +=item B<-e, --examples> |
| 201 | +
|
| 202 | +Show comprehensive usage examples and exit. |
| 203 | +
|
| 204 | +=item B<-h, -?, --help> |
| 205 | +
|
| 206 | +Print a brief help message and exit. |
| 207 | +
|
| 208 | +=item B<-m, --man> |
| 209 | +
|
| 210 | +Print the full manual page and exit. |
| 211 | +
|
| 212 | +=item B<-v, --version> |
| 213 | +
|
| 214 | +Print version information and exit. |
| 215 | +
|
| 216 | +=back |
| 217 | +
|
| 218 | +=head1 SUPPORTED REGEX FEATURES |
| 219 | +
|
| 220 | +=head2 Character Classes |
| 221 | +
|
| 222 | + [a-z] Lowercase letters |
| 223 | + [A-Z] Uppercase letters |
| 224 | + [0-9] Digits |
| 225 | + [abc] Specific characters |
| 226 | + [^a-z] Negated class |
| 227 | +
|
| 228 | +=head2 Escape Sequences |
| 229 | +
|
| 230 | + \d Digit [0-9] |
| 231 | + \w Word character [a-zA-Z0-9_] |
| 232 | + \s Whitespace |
| 233 | + \D Non-digit |
| 234 | + \W Non-word character |
| 235 | + \t \n \r Tab, newline, carriage return |
| 236 | +
|
| 237 | +=head2 Quantifiers |
| 238 | +
|
| 239 | + {n} Exactly n times |
| 240 | + {n,m} Between n and m times |
| 241 | + {n,} At least n times |
| 242 | + + One or more |
| 243 | + * Zero or more |
| 244 | + ? Zero or one |
| 245 | +
|
| 246 | +=head2 Groups and Alternation |
| 247 | +
|
| 248 | + (...) Capturing group |
| 249 | + (?:...) Non-capturing group |
| 250 | + | Alternation (cat|dog) |
| 251 | + \1 \2 Backreferences |
| 252 | +
|
| 253 | +=head2 Other |
| 254 | +
|
| 255 | + . Any character |
| 256 | + ^ Start anchor (stripped) |
| 257 | + $ End anchor (stripped) |
| 258 | +
|
| 259 | +=head1 EXAMPLES |
| 260 | +
|
| 261 | +Generate a 4-digit PIN: |
| 262 | +
|
| 263 | + random-string '\d{4}' |
| 264 | +
|
| 265 | +Generate 10 license plate style codes: |
| 266 | +
|
| 267 | + random-string '[A-Z]{3}\d{4}' --count 10 |
| 268 | +
|
| 269 | +Generate email addresses: |
| 270 | +
|
| 271 | + random-string '[a-z]{5,10}@(gmail|yahoo|hotmail)\.com' -c 5 |
| 272 | +
|
| 273 | +Generate with custom separator (comma-space): |
| 274 | +
|
| 275 | + random-string '[A-Z]{3}' -c 5 -S ', ' |
| 276 | +
|
| 277 | +Generate API keys using smart mode: |
| 278 | +
|
| 279 | + random-string 'AIza[0-9A-Za-z_-]{35}' -c 100 --smart |
| 280 | +
|
| 281 | +Generate patterns with repetition: |
| 282 | +
|
| 283 | + random-string '(\w{4})-\1' -c 3 |
| 284 | +
|
| 285 | +=head1 EXIT STATUS |
| 286 | +
|
| 287 | +Returns 0 on success, non-zero on error. |
| 288 | +
|
| 289 | +=head1 AUTHOR |
| 290 | +
|
| 291 | +Part of Data::Random::String::Matches distribution. |
| 292 | +
|
| 293 | +=head1 SEE ALSO |
| 294 | +
|
| 295 | +L<Data::Random::String::Matches> |
| 296 | +
|
| 297 | +=cut |
0 commit comments