In this chapter, we’ll look at various one-liners for performing calculations, such as
- finding minimum and maximum elements
- counting
- shuffling
- permuting words
- calculating dates and numbers.
You’ll also learn about the -a, -M, and -F command-line arguments, the $, special variable, and the @{[ ... ]} construction that lets you run code inside double quotes.
4.1 Check if a number is a prime(质数)
perl -lne '(1x$_) !~ /^1?$|^(11+?)\1+$/ && print "$_ is prime"'
比如说,数字7,字串是1111111,匹配11(11)+匹配不了,尝试111(111)+也不行,1111(1111)+也不行,所以是质数;同理,数字9,字串是111111111,匹配11(11)+匹配不上,但是匹配111(111)+可以,所以9是质数。
4.2 Print the sum of all fields on each line
perl -MList::Util=sum -alne 'print sum @F'
And you wish to find the sum of all these numbers. You can simply specify : as an argument to the -F switch, like this:
perl -MList::Util=sum -F: -alne 'print sum @F'
4.3 Print the sum of all fields on all lines
perl -MList::Util=sum -alne 'push @S,@F; END { print sum @S }'
Unfortunately, summing all fields on all lines using this solution creates a massive @S array. A better solution is to keep only the running sum, like this:
perl -MList::Util=sum -alne '$s += sum @F; END { print $s }'
4.4 Shuffle all fields on each line
echo a b c d | perl -MList::Util=shuffle -alne 'print "@{[shuffle @F]}"'
to this:
echo a b c d | perl -MList::Util=shuffle -alne 'print shuffle @F'
You can use the $, special variable to change the separator between array elements when they’re printed. For example, here’s what happens when I change the separator to a colon:
echo a b c d | perl -MList::Util=shuffle -alne '$,=":"; print shuffle @F'
You can also use the join function to join the elements of @F with a space:
perl -MList::Util=shuffle -alne 'print join " ", shuffle @F'
4.5 Find the numerically smallest element (minimum element) on each line
perl -MList::Util=min -alne 'print min @F'
4.6 Find the numerically smallest element (minimum element) over all lines
perl -MList::Util=min -alne '@M = (@M, @F); END { print min @M }'
If you’re using Perl 5.10 or later, you can do the same thing with this one-liner:
perl -MList::Util=min -alne '$min = min($min // (), @F); END { print $min }'
4.7 Find the numerically largest element (maximum
element) on each line
perl -MList::Util=max -alne 'print max @F'
4.8 Find the numerically largest element (maximum
element) over all lines
perl -MList::Util=max -alne '@M = (@M, @F); END { print max @M }'
If you’re using Perl 5.10 or later, you can use the // operator to shorten this one-liner:
perl -MList::Util=max -alne '$max = max($max // (), @F); END { print $max }'
4.9 Replace each field with its absolute value
perl -alne 'print "@{[map { abs } @F]}"'
4.10 Print the total number of fields on each line
perl -alne 'print scalar @F'
4.11 Print the total number of fields on each line,
followed by the line
perl -alne 'print scalar @F, " $_"'
4.12 Print the total number of fields on all lines
perl -alne '$t += @F; END { print $t }'
4.13 Print the total number of fields that match a pattern
perl -alne 'map { /regex/ && $t++ } @F; END { print $t || 0 }'
Looping would be a better approach:
perl -alne '$t += /regex/ for @F; END { print $t }'
Another way to do this is to use grep in the scalar context:
perl -alne '$t += grep /regex/, @F; END { print $t }'
4.14 Print the total number of lines that match a pattern
perl -lne '/regex/ && $t++; END { print $t || 0 }'
4.15 Print the number π
perl -Mbignum=bpi -le 'print bpi(21)'
The bignum library also exports the constant π, precomputed to 39 decimal places:
perl -Mbignum=PI -le 'print PI'
4.16 Print the number e
perl -Mbignum=bexp -le 'print bexp(1,21)'
For example, you could print the value of e 2 to 30 decimal places:
perl -Mbignum=bexp -le 'print bexp(2,31)'
As with π, bignum also exports the constant e precomputed to 39 decimal places:
perl -Mbignum=e -le 'print e'
4.17 Print UNIX time (seconds since January 1, 1970,00:00:00 UTC)
perl -le 'print time'
4.18 Print Greenwich Mean Time and local computer time
perl -le 'print scalar gmtime'
The built-in localtime function acts like gmtime, except it returns the computer’s local time when it’s used in the scalar context:
perl -le 'print scalar localtime'
You can slice this list (that is, extract elements from it) or print individual elements if you need just some part of the information it contains. For example, to print H:M:S, slice the elements 2, 1, and 0 from localtime, like this:
perl -le 'print join ":", (localtime)[2,1,0]'
To slice elements individually, specify a list of elements to extract, for instance [2,1,0]. Or slice them as a range:
perl -le 'print join ":", (localtime)[2..6]'
You can also use negative indexes to select elements from the opposite end of a list:
perl -le 'print join ":", (localtime)[-2, -3]'
4.19 Print yesterday’s date
perl -MPOSIX -le ' @now = localtime; $now[3] -= 1; print scalar localtime mktime @now'
4.20 Print the date 14 months, 9 days, and 7 seconds ago
perl -MPOSIX -le ' @now = localtime; $now[0] -=7; $now[3] -=9; $now[4] -= 14; print scalar localtime mktime @now'
4.21 Calculate the factorial
perl -MMath::BigInt -le 'print Math::BigInt->new(5)->bfac()'
Another way to calculate a factorial is to multiply the numbers from 1 to n together:
perl -le '$f = 1; $f *= $_ for 1..5; print $f'
4.22 Calculate the greatest common divisor
perl -MMath::BigInt=bgcd -le 'print bgcd(20,60,30)'
To calculate the gcd from a file or user’s input, use the -a commandline argument and pass the @F array to the bgcd function:
perl -MMath::BigInt=bgcd -anle 'print bgcd(@F)'
4.23 Calculate the least common multiple
perl -MMath::BigInt=blcm -le 'print blcm(35,20,8)'
4.24 Generate 10 random numbers between 5 and 15 (excluding 15)
perl -le 'print join ",", map { int(rand(15-5))+5 } 1..10'
4.25 Generate all permutations of a list
perl -MAlgorithm::Permute -le ' $l = [1,2,3,4,5];$p = Algorithm::Permute->new($l);print "@r" while @r = $p->next'
4.26 Generate the powerset
perl -MList::PowerSet=powerset -le ' @l = (1,2,3,4,5); print "@$_" for @{powerset(@l)} '
4.27 Convert an IP address to an unsigned integer
perl -le '$i=3; $u += ($_<<8*$i--) for "127.0.0.1" =~ /(\d+)/g; print $u'
Here are some more one-liners:
perl -le '$ip="127.0.0.1";$ip =~ s/(\d+)\.?/sprintf("%02x", $1)/ge;print hex($ip)'
You can also use unpack:
perl -le 'print unpack("N", 127.0.0.1)'
If you have a string with an IP (rather than a vstring), you first have to convert it to byte form with the function inet_aton:
perl -MSocket -le 'print unpack("N", inet_aton("127.0.0.1"))'
4.28 Convert an unsigned integer to an IP address
perl -MSocket -le 'print inet_ntoa(pack("N", 2130706433))'