Powerful command-line options

Gabor has his listicle for 7 of the most useful Perl command line options, although it’s really five after giving separate items for the -v / -V and -e / -E pairs. The others are -p, -n, and -i. I have my own list that I’d like to share. You might not like my list; go through perlrun to find your own. There’s also the book Perl One-Liners.

We used to teach one-liners in the Learning Perl and Intermediate Perl courses but interest was never that enthusiastic. System admins liked them but other people wanted to write programs they saved in files. The last item in Effective Perl Programming is “Use Perl one-liners to create mini programs” and is at the end of the chapter “Miscellany”.

Curiously, I was just this week wondering if Perl’s heritage matters anymore. We used to teach that Perl has its roots in sed, awk, and C. I’d wager that most of the students in my classes have never seen a sed or awk program and don’t understand the references. As a side note, I’ve often wondered if I should tag each Perl feature with the language the invented it.

-c checks syntax

I don’t have to run your program until I know it’s ready to go. There are many times where I want to know if the syntax is okay before I try the program. I want to discover the errors without actually interacting with the outside world. The -c does that:

% perl -c your_program.pl
your_program.pl syntax OK

How does my editor know about the syntax errors in my Perl program? It’s probably running perl -c in the background. This does everything up to perl running the program. If nothing bad happens, it reports that the syntax is okay. If not, I get the error output that I’ve seen far too often.

There’s a warning, though. The code in BEGIN blocks runs during the compilation phase.

-M loads a module

I can use modules in one-liners. I could type out use Module; in the argument to -e. That’s no good for a command line thing though.

Here I load Business::ISBN with -M:

% perl-latest -MBusiness::ISBN -e "print Business::ISBN->new(shift)->is_valid ? 'Valid' : 'Invalid'" 1491954329

I can import with -M too. The list goes after a = and the import arguments are comma separated:

% perl-latest -MBusiness::ISBN=valid_isbn_checksum -e "print valid_isbn_checksum(shift) ? 'Valid' : 'Invalid'" 1491954329

% perl-latest -MDigest::MD5=md5_hex -E 'say md5_hex(shift)' "The quick, grey llama"

% perl-latest -MDigest::SHA=sha256_hex -E 'say sha256_hex(shift)' "The quick, grey llama"

Mojolicious has a special one-liner module named ojo. Here’s a user-agent that makes a HEAD request and extracts the value of one of the response headers:

% perl-latest -Mojo -E "say h(shift)->headers->header(shift) // ''" https://www.mojolicious.org Server

Here’s one that’s a complete web application:

$ perl -Mojo -E 'a("/hello" => {text => "Hello Mojo!"})->start' daemon

These one-liners seem a bit burdensome for one shot uses, but I can also make these shell aliases (and your system might already have commands to do these tasks).

There’s also the -m which also loads a module but uses an empty list so nothing is imported. I never remember that one though.

-C turns on UTF-8

Perl has its roots in the ASCII world. That and it’s moderate sensitivity to backward compatibility means that some Unicode tasks are a bit cumbersome. The -C takes care of that.

There are several parts to -C that turn on various things. On it’s own, -C is really -CSDL:

  • S – UTF-8 for the standard filehandles
  • D – UTF-8 is the default for new filehandles
  • L – respect LC_ALL, LC_CTYPE, and LANG

That gets you most of the way. This doesn’t cover the command-line argument encoding. The A does that. By adding that I need to specify the other options that you want:

% perl -CSA -e "print for @ARGV" 🦈 🦕 🦖

I have some aliases to do various conversions for me. These use -M to load a module and -CS to set the encoding on the standard filehandles:

alias nfc="perl -MUnicode::Normalize -CS -ne 'print NFC(\$_)'"
alias nfd="perl -MUnicode::Normalize -CS -ne 'print NFD(\$_)'"
alias nfkd="perl -MUnicode::Normalize -CS -ne 'print NFKC(\$_)'"

There’s a Unicode primer at the end of Learning Perl that explains more of this.

-a autosplits a line

The -a and -F switches are my favorites and are often the strongest workers in my one-liners. They are a bit tricky until you get used to them.

Start with this program to simulate other interesting input that I might want to process:

% perl -E 'say join( q/ /, $_, $_**2, $_**3) for 1 .. 10'
1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
6 36 216
7 49 343
8 64 512
9 81 729
10 100 1000

“Piping” a program is the foundational Unix idea. Small programs do small jobs and you join the programs together to complete larger tasks. The output of one program becomes the input for the next program. For example, I can pipe the output of my powers program to cat -n to add line numbers:

$ perl -E 'say join( q/ /, $_, $_**2, $_**3) for 1 .. 10' | cat -n
     1	1 1 1
     2	2 4 8
     3	3 9 27
     4	4 16 64
     5	5 25 125
     6	6 36 216
     7	7 49 343
     8	8 64 512
     9	9 81 729
    10	10 100 1000

Now I want to extract the second column. Sure, I could do this with cut or some other utilities but it’s easy enough in Perl. The -a autosplits the line on whitespace and puts the results in @F. I can then select which column I want:

% perl -E 'say join( q/ /, $_, $_**2, $_**3) for 1 .. 10' | perl -a -nE 'say $F[1]'

That’s not all though. What if I don’t have a whitespace delimited file? I can change the field separator with the -F switch. The /etc/passwd file is colon-delimited and the fifth column is the name of the account:

% perl -aF: -E 'say $F[4]' /etc/passwd
Unprivileged User
System Administrator
System Services
Unix to Unix Copy Protocol
Task Gate Daemon
Network Services
Install Assistant

Piping that to sort is often useful:

$ perl -aF: -E 'say $F[4]' /etc/passwd | sort
AMaViS Daemon
ATS Server
Analytics Daemon
Apple Events User
Apple Remote Desktop
AppleEvents Daemon