You can do shell-like "here" documents in Perl.
Very useful for printing HTML in CGI.
print <<END; blah blah blah blee END print <<"END"; # text treated as double-quoted hello $there END print <<'END'; # text treated as single-quoted hello $there END print <<"" x 5; # next line double-quoted, x 5 This is a line to be printed output: blah blah blah blee hello there hello $there This is a line to be printed This is a line to be printed This is a line to be printed This is a line to be printed This is a line to be printed
Perl's builtin sort uses a quicksort algorithm. Used to rely on system's qsort() where possible, but recently went to own implementation due to bugs and inconsistencies among qsort()'s.
We can change the default sort comparison for the sort function by telling perl how to compare two items. We do this by declaring a subroutine that compares two things.
Subroutine you declare is to take two (global) values, $a and $b (not @_), and return a single value telling sort which one is greater or if they are equal. Don't recurse. Don't modify a or b.
#!/usr/bin/perl -w
# custom sort routine, compare two nums
sub by_num {
if ($a < $b) {
return(-1);
}
elsif ($a > $b) {
return(1);
}
else {
return(0);
}
}
@values = (7, 2, 3, 9, 1);
print "values were: @values\n";
@sorted = sort by_num @values;
print " values are: @sorted\n";
Note the routine could have been written as:
sub by_num {
return($a - $b);
}
Can even use "the spaceship operator" which does the three-way comparison for us:
sub by_num {
$a <=> $b; # return optional, automatically returns value of last expression
}
Even shorter, entire thing like this:
@sorted = sort { $a <=> $b } @values;
We also have a string-based spaceship-like operator, cmp for doing the same thing with strings.
Can sort hashes and arrays based on particular elements, break ties with other elements, etc.
#!/usr/local/bin/perl -w
@rec1 = ('Harvey', 'Smith', '567-8901');
@rec2 = ('Bill', 'Smith', '234-5678');
@rec3 = ('Harvey', 'Wallbanger', '345-6789');
@rec4 = ('Greg', 'Long', '123-4567');
@all = (\@rec1, \@rec2, \@rec3, \@rec4); # refs, otherwise flattened
print "original list:\n";
foreach $rec (@all) {
print "record: $$rec[1], $$rec[0]: $$rec[2]\n";
}
sub by_my_criteria {
return($a->[1] cmp $b->[1] or $a->[2] cmp $b->[2]);
}
@new = sort by_my_criteria @all;
print "sorted by last, phone:\n";
foreach $rec (@new) {
print "record: $$rec[1], $$rec[0]: $$rec[2]\n";
}
output:
original list:
record: Smith, Harvey: 567-8901
record: Smith, Bill: 234-5678
record: Wallbanger, Harvey: 345-6789
record: Long, Greg: 123-4567
sorted by last, phone:
record: Long, Greg: 123-4567
record: Smith, Bill: 234-5678
record: Smith, Harvey: 567-8901
record: Wallbanger, Harvey: 345-6789
Easy to rip through /etc/passwd or the distributed passwd table using Perl to read lines and the split function to tear them up.
But Perl also gives some functions for easier access to some cases.
#!/usr/bin/perl -w
@passwd_entry = getpwnam("gslong");
print "gslong's entry: @passwd_entry\n"; # prints out 9 fields
@passwd_entry = getpwuid(100);
print "gslong's entry (by uid): @passwd_entry\n"; # also nine
$user = getpwuid(100); # who's got UID 100?
print "user with uid 100 is: $user\n"; # note get just name in
# scalar context
$home = (getpwnam("gslong"))[7]; # pick off one needed
print "gslong's home is: $home\n";
Note Perl returns nine fields for each user, handles cases of systems that include "quota" and breaking GCOS into two fields, "comment" and "gcos".
($user, $passwd, $uid, $gid, $quota, $comment, $gcos, $home, $shell) = getpwuid(100);
Can also use another set of functions for sequential, rather than random-access order...
setpwent() initializes, getpwent() returns next user's entry, endpwent() terminates.
From text:
setpwent();
while (@list = getpwent()) {
($login, $home) = @list[0,7];
print "home = $home for user $login\n";
}
endpwent();
Perl supports a grep function to operate on lists.
#!/usr/bin/perl -w
# slurp up entire passwd file
open(PASS, "/etc/passwd") || die "can't read passwd\n";
chomp(@lines = <PASS>);
close(PASS);
# find lines with greg
@gregs = grep /^gslong:/, @lines;
# spit them out
foreach $greg (@gregs) {
print "found greg: $greg\n";
}
# find lines without greg
@not_gregs = grep !/^gslong:/, @lines;
grep returns list of matches in list context, but the number of matches in scalar context.
grep plays with $_ as it searches, you can use this to modify the array.
@found = grep { s/^Greg/Long/g } @list; # both found & list modified
# found only has lines where repl occured
What happens to above if use $found instead of @found to assign result?
Kinda related to grep, will apply a function to all elements of a list through $_. Returns a frequently useless list containing as many elements as were found in original list, return values of the function applied to each element.
@a = ("greg", "long", "super", "cool");
map { s/^[a-z]/x/ } @a; # list now: "xreg", "xong", etc
@a = ("greg", "long", "super", "cool");
map { $_ = ucfirst $_ } @a; # list now: "Greg", "Long", etc
# the following modifies each element of @lines
# return list is list of counts of how many sucessful changes
# were made on each line (undef in case of zero)
@counts = map { s/long/xxxx/g } @lines;
sub check_it {
if ($_ =~ /g$/) {
return("T");
}
else {
return("F");
}
}
@checked = map check_it, @lines;
# @checked now contains T or F in each position
# to note whether found g at end of element in @lines
Refer to "perlfunc" man page for complete list.
Math functions: abs, cos, sin, log, etc.
Random: srand (seeds, with time by default), rand (generates)
$x = rand(); # 0.0 <= x < 1.0 ; floats $x = rand(7); # 0.0 <= x < 7.0 ; floats $x = int(rand(10)); # 0 <= x < 10 ; integer values
Sockets: accept, bind, connect, listen, send, recv, getsockopt, setsockopt, etc.
Much more...