Posted on Leave a comment

Connect your Google Drive to Fedora Workstation

There are plenty of cloud services available where you can store important documents. Google Drive is undoubtedly one of the most popular. It offers a matching set of applications like Docs, Sheets, and Slides to create content. But you can also store arbitrary content in your Google Drive. This article shows you how to connect it to your Fedora Workstation.

Adding an account

Fedora Workstation lets you add an account either after installation during first startup, or at any time afterward. To add your account during first startup, follow the prompts. Among them is a choice of accounts you can add:

Online account listing

Select Google and a login prompt appears for you to login, so use your Google account information.

Online account login dialog

Be aware this information is only transmitted to Google, not to the GNOME project. The next screen asks you to grant access, which is required so your system’s desktop can interact with Google. Scroll down to review the access requests, and choose Allow to proceed.

You can expect to receive notifications on mobile devices and Gmail that a new device — your system — accessed your Google account. This is normal and expected.

Online account access request dialog

If you didn’t do this at first startup, or you need to re-add your account, open the Settings tool, and select Online Accounts to add the account. The Settings tool is available through the dropdown at right side of the Top Bar (the “gear” icon), or by opening the Overview and typing settings. Then proceed as described above.

Using the Files app with Google Drive

Open the Files app (formerly known as nautilus). Locations the Files app can access appear on the left side. Locate your Google account in the list.

When you select this account, the Files app shows the contents of your Google drive. Some files can be opened using your Fedora Workstation local apps, such as sound files or LibreOffice-compatible files (including Microsoft Office docs). Other files, such as Google app files like Docs, Sheets, and Slides, open using your web browser and the corresponding app.

Remember that if the file is large, it will take some time to receive over the network so you can open it.

You can also copy and paste files in your Google Drive storage from or to other storage connected to your Fedora Workstation. You can also use the built in functions to rename files, create folders, and organize them.

Be aware that the Files app does not refresh contents in real time. If you add or remove files from other Google connected devices like your mobile phone or tablet, you may need to hit Ctrl+R to refresh the Files app view.


Photo by Beatriz Pérez Moya on Unsplash.

Posted on Leave a comment

Submit a supplemental wallpaper for Fedora 32

Attention Fedora community members: Fedora is seeking submissions for supplemental wallpapers to be included with the Fedora 32 release. Whether you’re an active contributor, or have been looking for a easy way to get started contributing, submitting a wallpaper is a great way to help. Read on for more details.

Each release, the Fedora Design Team works with the community on a set of 16 additional wallpapers. Users can install and use these to supplement the standard wallpaper.

Dates and deadlines

The submission phase opened as of March 7, 2020 and ends March 21, 2020 at 23:59 UTC.

Important note: In some circumstances, submissions during the final hours may not get into the election, if there is insufficient time to do legal research. Please help by following the guidelines correctly, and submit only work under a correct license.

The voting phase will open the Monday following the close of submissions, March 23, 2020, and will be open until the end of the month on March 31, 2020 at 23:59 UTC.

How to contribute a wallpaper

Fedora uses the Nuancier application to manage the submissions and the voting process. To submit, you need a Fedora account. If you don’t have one, create one here in the Fedora Account System (FAS). To vote you must have a signed contributor agreement (also accessible in FAS) which only takes a few moments.

You can access Nuancier here along with detailed instructions for submissions.

Posted on Leave a comment

Using the Quarkus Framework on Fedora Silverblue – Just a Quick Look

Quarkus is a framework for Java development that is described on their web site as:

A Kubernetes Native Java stack tailored for OpenJDK HotSpot and GraalVM, crafted from the best of breed Java libraries and standards

https://quarkus.io/ – Feb. 5, 2020

Silverblue — a Fedora Workstation variant with a container based workflow central to its functionality — should be an ideal host system for the Quarkus framework.

There are currently two ways to use Quarkus with Silverblue. It can be run in a pet container such as Toolbox/Coretoolbox. Or it can be run directly in a terminal emulator. This article will focus on the latter method.

Why Quarkus

According to Quarkus.io: “Quarkus has been designed around a containers first philosophy. What this means in real terms is that Quarkus is optimized for low memory usage and fast startup times.” To achieve this, they employ first class support for Graal/Substrate VM, build time Metadata processing, reduction in reflection usage, and native image preboot. For details about why this matters, read Container First at Quarkus.

Prerequisites

A few prerequisites will need to configured before you can start using Quarkus. First, you need an IDE of your choice. Any of the popular ones will do. VIM or Emacs will work as well. The Quarkus site provides full details on how to set up the three major Java IDE’s (Eclipse, Intellij Idea, and Apache Netbeans). You will need a version of JDK installed. JDK 8, JDK 11 or any distribution of OpenJDK is fine. GrallVM 19.2.1 or 19.3.1 is needed for compiling down to native. You will also need Apache Maven 3.53+ or Gradle. This article will use Maven because that is what the author is more familiar with. Use the following command to layer Java 11 OpenJDK and Maven onto Silverblue:

$ rpm-ostree install java-11-openjdk* maven

Alternatively, you can download your favorite version of Java and install it directly in your home directory.

After rebooting, configure your JAVA_HOME and PATH environment variables to reference the new applications. Next, go to the GraalVM download page, and get GraalVM version 19.2.1 or version 19.3.1 for Java 11 OpenJDK. Install Graal as per the instructions provided. Basically, copy and decompress the archive into a directory under your home directory, then modify the PATH environment variable to include Graal. You use it as you would any JDK. So you can set it up as a platform in the IDE of your choice. Now is the time to setup the native image if you are going to use one. For more details on setting up your system to use Quarkus and the Quarkus native image, check out their Getting Started tutorial. With these parts installed and the environment setup, you can now try out Quarkus.

Bootstrapping

Quarkus recommends you create a project using the bootstrapping method. Below are some example commands entered into a terminal emulator in the Gnome shell on Silverblue.

$ mvn io.quarkus:quarkus-maven-plugin:1.2.1.Final:create \ -DprojectGroupId=org.jakfrost \ -DprojectArtifactId=silverblue-logo \ -DclassName="org.jakfrost.quickstart.GreetingResource" \ -Dpath="/hello"
$ cd silverblue-logo

The bootstrapping process shown above will create a project under the current directory with the name silverblue-logo. After this completes, start the application in development mode:

$ ./mvnw compile quarkus:dev

With the application running, check whether it responds as expected by issuing the following command:

$ curl -w '\n' http://localhost:8080/hello

The above command should print hello on the next line. Alternatively, test the application by browsing to http://localhost:8080/hello with your web browser. You should see the same lonely hello on an otherwise empty page. Leave the application running for the next section.

Injection

Open the project in your favorite IDE. If you are using Netbeans, simply open the project directory where the pom.xml file resides. Now would be a good time to have a look at the pom.xml file.

Quarkus uses ArC for its dependency injection. ArC is a dependency of quarkus-resteasy, so it is already part of the core Quarkus installation. Add a companion bean to the project by creating a java class in your IDE called GreetingService.java. Then put the following code into it:

import javax.enterprise.context.ApplicationScoped; @ApplicationScoped
public class GreetingService { public String greeting(String name) { return "hello " + name; } }

The above code is a verbatim copy of what is used in the injection example in the Quarkus Getting Started tutorial. Modify GreetingResource.java by adding the following lines of code:

import javax.inject.Inject;
import org.jboss.resteasy.annotations.jaxrs.PathParam; @Inject GreetingService service;//inject the service @GET //add a getter to use the injected service @Produces(MediaType.TEXT_PLAIN) @Path("/greeting/{name}") public String greeting(@PathParam String name) { return service.greeting(name); }

If you haven’t stopped the application, it will be easy to see the effect of your changes. Just enter the following curl command:

$ curl -w '\n' http://localhost:8080/hello/greeting/Silverblue

The above command should print hello Silverblue on the following line. The URL should work similarly in a web browser. There are two important things to note:

  1. The application was running and Quarkus detected the file changes on the fly.
  2. The injection of code into the app was very easy to perform.

The native image

Next, package your application as a native image that will work in a podman container. Exit the application by pressing CTRL-C. Then use the following command to package it:

$ ./mvnw package -Pnative -Dquarkus.native.container-runtime=podman

Now, build the container:

$ podman build -f src/main/docker/Dockerfile.native -t silverblue-logo/silverblue-logo

Now run it with the following:

$ podman run -i --rm -p 8080:8080 localhost/silverblue-logo/silverblue-logo

To get the container build to successfully complete, it was necessary to copy the /target directory and contents into the src/main/docker/ directory. Investigation as to the reason why is still required, and though the solution used was quick and easy, it is not an acceptable way to solve the problem.

Now that you have the container running with the application inside, you can use the same methods as before to verify that it is working.

Point your browser to the URL http://localhost:8080/ and you should get a index.html that is automatically generated by Quarkus every time you create or modify an application. It resides in the src/main/resources/META-INF/resources/ directory. Drop other HTML files in this resources directory to have Quarkus serve them on request.

For example, create a file named logo.html in the resources directory containing the below markup:

<!DOCTYPE html>
<!--
To change this license header, choose License Headers in Project Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
-->
<html> <head> <title>Silverblue</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <div> <img src="fedora-silverblue-logo.png" alt="Fedora Silverblue"/> </div> </body>
</html>

Next, save the below image alongside the logo.html file with the name fedora-silverblue-logo.png:

Now view the results at http://localhost:8080/logo.html.

Testing your application

Quarkus supports junit 5 tests. Look at your project’s pom.xml file. In it you should see two test dependencies. The generated project will contain a simple test, named GreetingResourceTest.java. Testing for the native file is only supported in prod mode. However, you can test the jar file in dev mode. These tests are RestAssured, but you can use whatever test library you wish with Quarkus. Use Maven to run the tests:

$ ./mvnw test

More details can be found in the Quarkus Getting Started tutorial.

Further reading and tutorials

Quarkus has an extensive collection of tutorials and guides. They are well worth the time to delve into the breadth of this microservices framework.

Quarkus also maintains a publications page that lists some very interesting articles on actual use cases of Quarkus. This article has only just scratched the surface of the topic. If what was presented here has piqued your interest, then follow the above links for more information.

Posted on Leave a comment

Fish – A Friendly Interactive Shell

Are you looking for an alternative to bash? Are you looking for something more user-friendly? Then look no further because you just found the golden fish!

Fish (friendly interactive shell) is a smart and user-friendly command line shell that works on Linux, MacOS, and other operating systems. Use it for everyday work in your terminal and for scripting. Scripts written in fish are less cryptic than their equivalent bash versions.

Fish’s user-friendly features

  • Suggestions
    Fish will suggest commands that you have written before. This boosts productivity when typing same commands often.
  • Sane scripting
    Fish avoids using cryptic characters. This provides a clearer and friendlier syntax.
  • Completion based on man pages
    Fish will autocomplete parameters based on the the command’s man page.
  • Syntax highlighting
    Fish will highlight command syntax to make it visually friendly.

Installation

Fedora Workstation

Use the dnf command to install fish:

$ sudo dnf install fish

Make fish your default shell by installing the util-linux-user package and then running the chsh (change shell) command with the appropriate parameters:

$ sudo dnf install util-linux-user
$ chsh -s /usr/bin/fish

You will need to log out and back in for this change to take effect.

Fedora Silverblue

Because this is not GUI application, you will need to layer it using rpm-ostree. Use the following command to install fish on Fedora Silverblue:

$ rpm-ostree install fish

On Fedora Silverblue you will need to reboot your PC to switch to the new ostree image.

If you want to make fish your main shell on Fedora Silverblue, the easiest way is to update the /etc/passwd file. Find your user and change /bin/bash to /usr/bin/fish.

You will need root privileges to edit the /etc/passwd file. Also you will need to log out and back in for this change to take effect.

Configuration

The per-user configuration file for fish is ~/.config/fish/config.fish. To make configuration changes for all users, edit /etc/fish/config.fish instead.

The per-user configuration file must be created manually. The installation scripts will not create ~/.config/fish/config.fish.

Here are a couple configuration examples shown alongside their bash equivalents to get you started:

Creating aliases

  • ~/.bashrc:
    alias ll=’ls -lh’
  • ~/.config/fish/config.fish:
    alias ll=’ls -lh’

Setting environment variables

  • ~/.bashrc:
    export PATH=$PATH:~/bin
  • ~/.config/fish/config.fish:
    set -gx PATH $PATH ~/bin

Working with fish

When fish is configured as your default shell, the command prompt will look similar to what is shown in the below image. If you haven’t configured fish to be your default shell, just run the fish command to start it in your current terminal session.

As you start typing commands, you will notice the syntax highlighting:

Cool, isn’t it? 🙂

You will also see commands being suggested as you type. For example, start typing the previous command a second time:

Notice the gray text that appears as you type. The gray text is fish suggesting the command you wrote before. To autocomplete it, just press CTRL+F.

Get argument suggestions based on the preceding command’s man page by typing a dash () and then the TAB key:

If you press TAB once, it will show you the first few suggestions (or every suggestion, if there are only a few arguments available). If you press TAB a second time, it will show you all suggestions. If you press TAB three times consecutively, it will switch to interactive mode and you can select an argument using the arrow keys.

Otherwise, fish works similar to most other shells. The remaining differences are well documented. So it shouldn’t be difficult to find other features that you may be interested in.

Make fish even more powerful

Make the fish even more powerful with powerline. Powerline adds command execution time, colored git status, current git branch and much more to fish’s interface.

Before installing powerline for fish, you must install Oh My Fish. Oh My Fish extends fish’s core infrastructure to enable the installation of additional plugins. The easiest way to install Oh My Fish is to use the curl command:

> curl -L https://get.oh-my.fish | fish

If you don’t want to pipe the installation commands directly to curl, see the installation section of Oh My Fish’s README for alternative installation methods.

Fish’s powerline plugin is bobthefish. Bobthefish requires the powerline-fonts package.

On Fedora Workstation:

> sudo dnf install powerline-fonts

On Fedora Silverblue:

> rpm-ostree install powerline-fonts

On Fedora Silverblue you will have to reboot to complete the installation of the fonts.

After you have installed the powerline-fonts package, install bobthefish:

> omf install bobthefish

Now you can experience the full awesomeness of fish with powerline:

Additional resources

Check out these web pages to learn even more about fish:

Posted on Leave a comment

Manage tasks and projects on Fedora with Taskwarrior

There are a multitude of applications to manage your todo list. One of these apps is Taskwarrior, it allows you to manage your task in the terminal without a GUI. This article will show you how to get started using it.

What is Taskwarrior?

Taskwarrior is CLI task manager and organizer. It is flexible, fast, and unobtrusive. It does its job then gets out of your way.

Taskwarrior uses $HOME/.taskrc and $HOME/.task to store your settings and tasks respectively.

Getting started with Taskwarrior

It’s easy to use the Taskwarrior to add your daily missions. These are some simple commands. To add tasks:

$ task add buy milk Created task 1. $ task add buy eggs Created task 2. $ task add bake cake Created task 3.

To list your tasks, you can use the task command on its own for the simplest listing:

$ task ID Age Description Urg 1 17s buy milk 0 2 14s buy eggs 0 3 11s bake cake 0 3 tasks.

To mark a task as complete, use the done keyword:

$ task 1 done Completed task 1 'buy milk'. Completed 1 task.
$ task 2 done Completed task 2 'buy eggs'. Completed 1 task.
$ task [task next] ID Age Description Urg 1 57s bake cake 0 1 task

Diving deeper into Taskwarrior

Priority management

Taskwarrior (task) is designed to help prioritize your tasks. To do this, task has multiple implicit and explicit variables it can use to determine an “Urgency” value.

Consider the following list.

$ task [task next] ID Age Description Urg 1 2min buy eggs 0 2 2min buy flour 0 3 2min bake cake 0 4 2min pay rent 0 5 3s install fedora 0 5 tasks

One could argue that paying your rent and installing Fedora have a higher priority than baking a cake. You can tell task about this by using the pri modifier.

$ task 4 mod pri:H Modifying task 4 'pay rent'. Modified 1 task.
$ task 5 mod pri:M Modifying task 5 'install fedora'. Modified 1 task.
$ task [task next] ID Age P Description Urg  4 4min H pay rent 6
 5 2min M install fedora 3.9 1 4min buy eggs 0 2 4min buy flour 0 3 4min bake cake 0 5 tasks

Rent is very important, it has a due date that we need to pay it by, such as within 3 days from the 1st of the month. You can tell task this by using the due modifier.

$ task 4 mod due:3rd Modifying task 4 'pay rent'. Modified 1 task.
$ task [task next] ID Age P Due Description Urg 4 12min H 2d pay rent 13.7 5 10min M install fedora 3.9 1 12min buy eggs 0 2 12min buy flour 0 3 12min bake cake 0 5 tasks
$ date Sat Feb 29 11:59:29 STD 2020

Because the 3rd of next month is nearby, the urgency value of rent has skyrocketed, and will continue to do so once we have reached and passed the due date.

However, not all tasks need to be done right away. Say for example you don’t want to worry about paying your rent until it is posted on the first of the month. You can tell taskwarrior about this using the wait modifier. (Hint: in the following example, som is short for “start of month,” one of the shortcuts taskwarrior understands.)

$ task 4 mod wait:som Modifying task 4 'pay rent'. Modified 1 task.
$ task [task next] ID Age P Description Urg 5 14min M install fedora 3.9 1 16min buy eggs 0 2 16min buy flour 0 3 16min bake cake 0 4 tasks

You will no longer be able to see the pay rent task until the start of the month. You can view waiting tasks by using task waiting:

$ task waiting ID Age P Wait Remaining Due Description 4 18min H 2020-03-01 11h 2020-03-03 pay rent 1 task

There are a few other modifiers you can define. Schedule and until will place a “start” date and remove a task after a date respectfully.

You may have tasks that require other tasks to be completed. To add a dependency for other tasks, use the dep modifier:

$ task [task next] ID Age P Description Urg 5 30min M install fedora 3.9 1 33min buy eggs 0 2 33min buy flour 0 3 33min bake cake 0 4 tasks
$ task 3 mod dep:1,2 Modifying task 3 'bake cake'. Modified 1 task. $ task [task next] ID Age Deps P Description Urg 1 33min buy eggs 8 2 33min buy flour 8 5 31min M install fedora 3.9 3 33min 1 2 bake cake -5 4 tasks

This will modify the priorities of any tasks that is blocking a task. Now buying eggs and flour is more urgent because it is preventing you from performing a task.

Annotations

You can add notes to a task using task <number> annotate:

$ task 3 anno No blueberries Annotating task 3 'bake cake'. Annotated 1 task. $ task [task next] ID Age Deps P Description Urg 1 1h buy eggs 8 2 1h buy flour 8 5 1h M install fedora 3.9 3 1h 1 2 bake cake -4.2 2020-02-29 No blueberries 4 tasks

Organizing tasks

Tasks can being assigned to projects and tags by using the project modifier and adding a tag using the + sign followed by the tag name, such as +problem.

Putting it all together

You can combine everything you learned to create a task in one line with all the required options.

$ task add Write Taskwarrior post \
pri:M due:1m wait:som until:due+2w sche:15th \
project:magazine +taskwarrior +community +linux Created task 6. The project 'magazine' has changed. Project 'magazine' is 0% complete (1 task remaining).
$ task 6 No command specified - assuming 'information'. Name Value ID 6 Description Write Taskwarrior post Status Waiting Project magazine Entered 2020-02-29 13:50:27 (6s) Waiting until 2020-03-01 00:00:00 Scheduled 2020-03-15 00:00:00 Due 2020-03-30 14:50:27 Until 2020-04-13 14:50:27 Last modified 2020-02-29 13:50:27 (6s) Tags taskwarrior community linux Virtual tags SCHEDULED TAGGED UDA UNBLOCKED UNTIL WAITING YEAR LATEST PROJECT PRIORITY UUID 27768737-f6a2-4515-af9d-4f58773c76a5 Urgency 5.3 Priority M

Installing Taskwarrior on Fedora

Taskwarrior is available in the default Fedora repository. To install it use this command with sudo:

$ sudo dnf install task

For rpm-ostree based distributions like Fedora Silverblue:

$ sudo rpm-ostree install task 

Tips and tricks

  • Taskwarrior has a hook system, meaning that there are many tools you can plug in, such as bugwarrior!
  • Taskwarrior can connect to a taskserver for server/client setups. (This is left as an exercise for the reader for now.)

Photo by Bogdan Kupriets on Unsplash.

Posted on Leave a comment

Demonstrating PERL with Tic-Tac-Toe, Part 2

The astute observer may have noticed that PERL is misspelled. In a March 1, 1999 interview with Linux Journal, Larry Wall explained that he originally intended to include the letter “A” from the word “And” in the title “Practical Extraction And Report Language” such that the acronym would correctly spell the word PEARL. However, before he released PERL, Larry heard that another programming language had already taken that name. To resolve the name collision, he dropped the “A”. The acronym is still valid because title case and acronyms allow articles, short prepositions and conjunctions to be omitted (compare for example the acronym LASER).

Name collisions happen when distinct commands or variables with the same name are merged into a single namespace. Because Unix commands share a common namespace, two commands cannot have the same name.

The same problem exists for the names of global variables and subroutines within programs written in languages like PERL. This is an especially significant problem when programmers try to collaborate on large software projects or otherwise incorporate code written by other programmers into their own code base.

Starting with version 5, PERL supports packages. Packages allow PERL code to be modularized with unique namespaces so that the global variables and functions of the modularized code will not collide with the variables and functions of another script or module.

Shortly after its release, PERL5 software developers all over the world began writing software modules to extend PERL’s core functionality. Because many of those developers (currently about 15,000) have made their work freely available on the Comprehensive Perl Archive Network (CPAN), you can easily extend the functionality of PERL on your PC so that you can perform very advanced and complex tasks with just a few commands.

The remainder of this article builds on the previous article in this series by demonstrating how to install, use and create PERL modules on Fedora Linux.

An example PERL program

See the example program from the previous article below, with a few lines of code added to import and use some modules named chip1, chip2 and chip3. It is written in such a way that the program should work even if the chip modules cannot be found. Future articles in this series will build on the below script by adding the additional modules named chip2 and chip3.

You should be able to copy and paste the below code into a plain text file and use the same one-liner that was provided in the previous article to strip the leading numbers.

00 #!/usr/bin/perl
01 02 use strict;
03 use warnings;
04 05 use feature 'state';
06 07 use constant MARKS=>[ 'X', 'O' ];
08 use constant HAL9K=>'O';
09 use constant BOARD=>'
10 ┌───┬───┬───┐
11 │ 1 │ 2 │ 3 │
12 ├───┼───┼───┤
13 │ 4 │ 5 │ 6 │
14 ├───┼───┼───┤
15 │ 7 │ 8 │ 9 │
16 └───┴───┴───┘
17 ';
18 19 use lib 'hal';
20 use if -e 'hal/chip1.pm', 'chip1';
21 use if -e 'hal/chip2.pm', 'chip2';
22 use if -e 'hal/chip3.pm', 'chip3';
23 24 sub get_mark {
25 my $game = shift;
26 my @nums = $game =~ /[1-9]/g;
27 my $indx = (@nums+1) % 2;
28 29 return MARKS->[$indx];
30 }
31 32 sub put_mark {
33 my $game = shift;
34 my $mark = shift;
35 my $move = shift;
36 37 $game =~ s/$move/$mark/;
38 39 return $game;
40 }
41 42 sub get_move {
43 return (<> =~ /^[1-9]$/) ? $& : '0';
44 }
45 46 PROMPT: {
47 no strict;
48 no warnings;
49 50 state $game = BOARD;
51 52 my $mark;
53 my $move;
54 55 print $game;
56 57 if (defined &get_victor) {
58 my $victor = get_victor $game, MARKS;
59 if (defined $victor) {
60 print "$victor wins!\n";
61 complain if ($victor ne HAL9K);
62 last PROMPT;
63 }
64 }
65 66 last PROMPT if ($game !~ /[1-9]/);
67 68 $mark = get_mark $game;
69 print "$mark\'s move?: ";
70 71 if ($mark eq HAL9K and defined &hal_move) {
72 $move = hal_move $game, $mark, MARKS;
73 print "$move\n";
74 } else {
75 $move = get_move;
76 }
77 $game = put_mark $game, $mark, $move;
78 79 redo PROMPT;
80 }

Once you have the above code downloaded and working, create a subdirectory named hal under the same directory that you put the above program. Then copy and paste the below code into a plain text file and use the same procedure to strip the leading numbers. Name the version without the line numbers chip1.pm and move it into the hal subdirectory.

00 # basic operations chip
01 02 package chip1;
03 04 use strict;
05 use warnings;
06 07 use constant MAGIC=>'
08 ┌───┬───┬───┐
09 │ 2 │ 9 │ 4 │
10 ├───┼───┼───┤
11 │ 7 │ 5 │ 3 │
12 ├───┼───┼───┤
13 │ 6 │ 1 │ 8 │
14 └───┴───┴───┘
15 ';
16 17 use List::Util 'sum';
18 use Algorithm::Combinatorics 'combinations';
19 20 sub get_moves {
21 my $game = shift;
22 my $mark = shift;
23 my @nums;
24 25 while ($game =~ /$mark/g) {
26 push @nums, substr(MAGIC, $-[0], 1);
27 }
28 29 return @nums;
30 }
31 32 sub get_victor {
33 my $game = shift;
34 my $marks = shift;
35 my $victor;
36 37 TEST: for (@$marks) {
38 my $mark = $_;
39 my @nums = get_moves $game, $mark;
40 41 next unless @nums >= 3;
42 for (combinations(\@nums, 3)) {
43 my @comb = @$_;
44 if (sum(@comb) == 15) {
45 $victor = $mark;
46 last TEST;
47 }
48 }
49 }
50 51 return $victor;
52 }
53 54 sub hal_move {
55 my $game = shift;
56 my @nums = $game =~ /[1-9]/g;
57 my $rand = int rand @nums;
58 59 return $nums[$rand];
60 }
61 62 sub complain {
63 print "Daisy, Daisy, give me your answer do.\n";
64 }
65 66 sub import {
67 no strict;
68 no warnings;
69 70 my $p = __PACKAGE__;
71 my $c = caller;
72 73 *{ $c . '::get_victor' } = \&{ $p . '::get_victor' };
74 *{ $c . '::hal_move' } = \&{ $p . '::hal_move' };
75 *{ $c . '::complain' } = \&{ $p . '::complain' };
76 }
77 78 1;

The first thing that you will probably notice when you try to run the program with chip1.pm in place is an error message like the following (emphasis added):

$ Can't locate Algorithm/Combinatorics.pm in @INC (you may need to install the Algorithm::Combinatorics module) (@INC contains: hal /usr/local/lib64/perl5/5.30 /usr/local/share/perl5/5.30 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5) at hal/chip1.pm line 17.
BEGIN failed--compilation aborted at hal/chip1.pm line 17.
Compilation failed in require at /usr/share/perl5/if.pm line 15.
BEGIN failed--compilation aborted at game line 18.

When you see an error like the one above, just use the dnf command to search Fedora’s package repository for the name of the system package that provides the needed PERL module as shown below. Note that the module name and path from the above error message have been prefixed with */ and then surrounded with single quotes.

$ dnf provides '*/Algorithm/Combinatorics.pm'
...
perl-Algorithm-Combinatorics-0.27-17.fc31.x86_64 : Efficient generation of combinatorial sequences
Repo : fedora
Matched from:
Filename : /usr/lib64/perl5/vendor_perl/Algorithm/Combinatorics.pm

Hopefully it will find the needed package which you can then install:

$ sudo dnf install perl-Algorithm-Combinatorics

Once you have all the needed modules installed, the program should work.

How it works

This example is admittedly quite contrived. Nothing about Tic-Tac-Toe is complex enough to need a CPAN module. To demonstrate installing and using a non-standard module, the above program uses the combinations library routine from the Algorithm::Combinatorics module to generate a list of the possible combinations of three numbers from the provided set. Because the board numbers have been mapped to a 3×3 magic square, any set of three numbers that sum to 15 will be aligned on a column, row or diagonal and will therefore be a winning combination.

Modules are imported into a program with the use and require commands. The only difference between them is that the use command automatically calls the import subroutine (if one exists) in the module being imported. The require command does not automatically call any subroutines.

Modules are just files with a .pm extension that contain PERL subroutines and variables. They begin with the package command and end with 1;. But otherwise, they look like any other PERL script. The file name should match the package name. Package and file names are case sensitive.

Beware that when you are reading online documentation about PERL modules, the documentation often veers off into topics about classes. Classes are built on modules, but a simple module does not have to adhere to all the restrictions that apply to classes. When you start seeing words like method, inheritance and polymorphism, you are reading about classes, not modules.

There are two subroutine names that are reserved for special use in modules. They are import and unimport and they are called by the use and no directives respectively.

The purpose of the import and unimport subroutines is typically to alias and unalias the module’s subroutines in and out of the calling namespace respectively. For example, line 17 of chip1.pm shows the sum subroutine being imported from the List::Util module.

The constant module, as used on lines 07 of chip1.pm, is also altering the caller’s namespace (chip1), but rather than importing a predefined subroutine, it is creating a special type of variable.

All the identifiers immediately following the use keywords in the above examples are modules. On my system, many of them can be found under the /usr/share/perl5 directory.

Notice that the above error message states “@INC contains:” followed by a list of directories. INC is a special PERL variable that lists, in order, the directories from which modules should be loaded. The first file found with a matching name will be used.

As demonstrated on line 19 of the Tic-Tac-Toe game, the lib module can be used to update the list of directories in the INC variable.

The chip1 module above provides an example of a very simple import subroutine. In most cases you will want to use the import subroutine that is provided by the Exporter module rather than implementing your own. A custom import subroutine is used in the above example to demonstrate the basics of what it does. Also, the custom implementation makes it easy to override the subroutine definitions in later examples.

The import subroutine shown above reveals some of the hidden magic that makes packages work. All variables that are both globally scoped (that is, created outside of any pair of curly brackets) and dynamically scoped (that is, not prefixed with the keywords my or state) and all global subroutines are automatically prefixed with a package name. The default package name if no package command has been issued is main.

By default, the current package is assumed when an unqualified variable or subroutine is used. When get_move is called from the PROMPT block in the above example, main::get_move is assumed because the PROMPT block exists in the main package. Likewise, when get_moves is called from the get_victor subroutine, chip1::get_moves is assumed because get_victor exists in the chip1 package.

If you want to access a variable or subroutine that exists in a different package, you either have to use its fully qualified name or create a local alias that refers to the desired subroutine.

The import subroutine shown above demonstrates how to create subroutine aliases that refer to subroutines in other packages. On lines 73-75, the fully qualified names for the subroutines are being constructed and then the symbol table name for the subroutine in the calling namespace (the package in which the use statement is being executed) is being assigned the reference of the subroutine in the local package (the package in which the import subroutine is defined).

Notice that subroutines, like variables, have sigils. The sigil for subroutines is the ampersand symbol (&). In most contexts, the sigil for subroutines is optional. When working with references (as shown on lines 73-75 of the import subroutine) and when checking if a subroutine is defined (as shown on lines 57 and 71 of the PROMPT block), the sigil for subroutines is required.

The import subroutine shown above is just a bare minimum example. There is a lot that it doesn’t do. In particular, a proper import subroutine would not automatically import any subroutines or variables. Normally, the user would be expected to provide a list of the routines to be imported on the use line and that list is available to the import subroutine in the @_ array.

Final notes

Lines 25-27 of chip1.pm provide a good example of PERL’s dense notation problem. With just a couple of lines code, the board numbers on which a given mark has been placed can be determined. But does the statement within the conditional clause of the while loop perform the search from the beginning of the game variable on each iteration? Or does it continue from where it left off each time? PERL correctly guesses that I want it to provide the position ($-[0]) of the next mark, if any exits, on each iteration. But exactly what it will do can be very difficult to determine just by looking at the code.

The last things of note in the above examples are the strict and warnings directives. They enable extra compile-time and runtime debugging messages respectively. Many PERL programmers recommend always including these directives so that programming errors are more likely to be spotted. The downside of having them enabled is that some complex code will sometimes cause the debugger to erroneously generate unwanted output. Consequently, the strict and/or warnings directives may need to be disabled in some code blocks to get your program to run correctly as demonstrated on lines 67 and 68 of the example chip1 module. The strict and warnings directives have nothing to do with the program and they can be omitted. Their only purpose is to provide feedback to the program developer.

Posted on Leave a comment

Fedora’s gaggle of desktops

There are 38 different desktops or window managers in Fedora 31. You could try a different one every day for a month, and still have some left over. Some have very few features. Some have so many features they are called a desktop environment. This article can’t go into detail on each, but it’s interesting to see the whole list in one place.

Criteria for desktops

To be on this list, the desktop must show up on the desktop manager’s selection list. If the desktop has more than one entry in the desktop manager list, they are counted just as that one desktop. An example is “GNOME”, “GNOME Classic” and “GNOME (Wayland).” These all show up on the desktop manager list, but they are still just GNOME.

List of desktops

9wm

Emulation of the Plan 9 window manager 8 1/2 dnf install 9wm

awesome

Highly configurable, framework window manager for X. Fast, light and extensible https://fedoramagazine.org/5-cool-tiling-window-managers/ dnf install awesome

blackbox

Very small and fast Window Manager Fedora uses the maintained fork on github dnf install blackbox

bspwm

A tiling window manager based on binary space partitioning https://github.com/windelicato/dotfiles/wiki/bspwm-for-dummies dnf install bspwm

byobu

Light-weight, configurable window manager built upon GNU screen dnf install byobu

Cinnamon

Cinnamon provides a desktop with a traditional layout, advanced features, easy to use, powerful and flexible. https://projects.linuxmint.com/cinnamon/ https://opensource.com/article/19/12/cinnamon-linux-desktop dnf group install "Cinnamon Desktop"

cwm

Calm Window Manager by OpenBSD project https://steemit.com/technology/@jamesdeagle/the-calm-window-manager-cwm-a-quick-start-guide dnf install cwm

Deepin

Deepin desktop is the desktop environment released with deepin (the linux distribution). It aims at being elegant and easy to use. dnf group install "Deepin Desktop" (optional) dnf group install "Deepin Desktop Office" "Media packages for Deepin Desktop"

dwm

Dynamic window manager for X https://fedoramagazine.org/lets-try-dwm-dynamic-window-manger/ https://fedoramagazine.org/5-cool-tiling-window-managers/ dnf install dwm (optional) dnf install dwm-user

enlightenment

Enlightenment window manager https://opensource.com/article/19/12/linux-enlightenment-desktop dnf install enlightenment

e16

The Enlightenment window manager, DR16 dnf install e16 (optional) dnf install e16-epplets e16-keyedit e16-themes

fluxbox

Window Manager based on Blackbox dnf install fluxbox (optional) dnf install fluxbox-pulseaudio fluxbox-vim-syntax

fvwm

Highly configurable multiple virtual desktop window manager http://www.fvwm.org/ https://opensource.com/article/19/12/fvwm-linux-desktop dnf install fvwm

GNOME

GNOME is a highly intuitive and user friendly desktop environment. * both X11 and wayland https://opensource.com/article/19/12/gnome-linux-desktop https://fedoramagazine.org/3-simple-and-useful-gnome-shell-extensions/ dnf group install "GNOME" (optional but large) dnf group install "Fedora Workstation"

herbstluftwm

A manual tiling window manager https://opensource.com/article/19/12/herbstluftwm-linux-desktop dnf install herbstluftwm (optional) dnf install herbstluftwm-zsh herbstluftwm-fish

i3

Improved tiling window manager https://fedoramagazine.org/getting-started-i3-window-manager/ https://fedoramagazine.org/using-i3-with-multiple-monitors/ dnf install i3 (optional) dnf install i3-doc i3-ipc

icewm

Window manager designed for speed, usability, and consistency https://fedoramagazine.org/icewm-a-really-cool-desktop/ dnf install icewm (optional) dnf install icewm-minimal-session

jwm

Joe's Window Manager https://opensource.com/article/19/12/joes-window-manager-linux-desktop dnf install jwm

KDE Plasma Desktop

The KDE Plasma Workspaces, a highly-configurable graphical user interface which includes a panel, desktop, system icons and desktop widgets, and many powerful KDE applications. * both X11 and wayland https://opensource.com/article/19/12/linux-kde-plasma https://fedoramagazine.org/installing-kde-plasma-5/ dnf group install "KDE Plasma Workspaces" (optional) dnf group install "KDE Applications" "KDE Educational applications" "KDE Multimedia support" "KDE Office" "KDE Telepathy" (optional for wayland) dnf install kwin-wayland plasma-workspace-wayland

lumina

A lightweight, portable desktop environment https://opensource.com/article/19/12/linux-lumina-desktop dnf install lumina-desktop (optional) dnf install lumina-*

LXDE

LXDE is a lightweight X11 desktop environment designed for computers with low hardware specifications like netbooks, mobile devices or older computers. https://opensource.com/article/19/12/lxqt-lxde-linux-desktop dnf group install "LXDE Desktop" (optional) dnf group install "LXDE Office" "Multimedia support for LXDE"

LXQt

LXQt is a lightweight X11 desktop environment designed for computers with low hardware specifications like netbooks, mobile devices or older computers. https://opensource.com/article/19/12/lxqt-lxde-linux-desktop dnf group install "LXQt Desktop" (optional) dnf group install "LXQt Office" "Multimedia support for LXQt"

MATE

MATE Desktop is based on GNOME 2 and provides a powerful graphical user interface for users who seek a simple easy to use traditional desktop interface. https://opensource.com/article/19/12/mate-linux-desktop https://fedoramagazine.org/installing-another-desktop/ dnf group install "MATE Desktop" (optional) dnf group install "MATE Applications"

musca

A simple dynamic window manager fox X dnf install musca

openbox

A highly configurable and standards-compliant X11 window manager https://opensource.com/article/19/12/openbox-linux-desktop https://fedoramagazine.org/openbox-fedora/ dnf install openbox (optional) dnf install openbox-kde openbox-theme-mistral-thin-dark

Pantheon

The Pantheon desktop environment is the DE that powers elementaryOS. https://github.com/elementary https://opensource.com/article/19/12/pantheon-linux-desktop dnf group install "Pantheon Desktop" (optional) dnf install elementary-capnet-assist elementary-greeter elementary-shortcut-overlay

pekwm

A small and flexible window manager https://opensource.com/article/19/12/pekwm-linux-desktop dnf install pekwm

qtile

A pure-Python tiling window manager https://fedoramagazine.org/5-cool-tiling-window-managers/ dnf install qtile

ratpoison

Minimalistic window manager https://opensource.com/article/19/12/ratpoison-linux-desktop dnf install ratpoison

sawfish

An extensible window manager for the X Window System dnf install sawfish (optional) dnf install sawfish-pager

spectrwm

Minimalist tiling window manager written in C dnf install spectrwm

Sugar

A software playground for learning about learning. * Possibly the most unique desktop of this list. dnf group install "Sugar Desktop Environment" (optional) dnf group install "Additional Sugar Activities"

sway

i3-compatible window manager for Wayland * Wayland only https://fedoramagazine.org/setting-up-the-sway-window-manager-on-fedora/ https://fedoramagazine.org/5-cool-tiling-window-managers/ dnf install sway

twm

X.Org X11 twm window manager https://en.wikipedia.org/wiki/Twm https://opensource.com/article/19/12/twm-linux-desktop dnf install xorg-x11-twm

WindowMaker

A fast, feature rich Window Manager https://opensource.com/article/19/12/linux-window-maker-desktop dnf install WindowMaker (optional) dnf install WindowMaker-extra

wmx

A really simple window manager for X dnf install wmx

XFCE

A lightweight desktop environment that works well on low end machines. https://opensource.com/article/19/12/xfce-linux-desktop dnf group install "Xfce Desktop" (optional) dnf group install "Applications for the Xfce Desktop" "Extra plugins for the Xfce panel" "Multimedia support for Xfce" "Xfce Office"

xmonad

A tiling window manager dnf install xmonad (optional) dnf install xmonad-mate

Photo by Annie Spratt on Unsplash.

Posted on Leave a comment

How we decide when to release Fedora

Open source projects can use a variety of different models for deciding when to put out a release. Some projects release on a set schedule. Others decide on what the next release should contain and release whenever that is ready. Some just wake up one day and decide it’s time to release. And other projects go for a rolling release model, avoiding the question entirely.

For Fedora, we go with a schedule-based approach. Releasing twice a year means we can give our contributors time to implement large changes while still keeping on the leading edge. Targeting releases for the end of April and the end of October gives everyone predictability: contributors, users, upstreams, and downstreams.

But it’s not enough to release whatever’s ready on the scheduled date. We want to make sure that we’re releasing quality software. Over the years, the Fedora community has developed a set of processes to help ensure we can meet both our time and and quality targets.

Changes process

Meeting our goals starts months before the release. Contributors propose changes through our Changes process, which ensures that the community has a chance to provide input and be aware of impacts. For changes with a broad impact (called “system-wide changes”), we require a contingency plan that describes how to back out the change if it’s broken or won’t be ready in time. In addition, the change process includes providing steps for testing. This helps make sure we can properly verify the results of a change.

Change proposals are due 2-3 months before the beta release date. This gives the community time to evaluate the impact of the change and make adjustments necessary. For example, a new compiler release might require other package maintainers to fix bugs exposed by the new compiler or to make changes that take advantage of new capabilities.

A few weeks before the beta and final releases, we enter a code freeze. This ensures a stable target for testing. Bugs identified as blockers and non-blocking bugs that are granted a freeze exception are updated in the repo, but everything else must wait. The freeze lasts until the release.

Blocker and freeze exception process

In a project as large as Fedora, it’s impossible to test every possible combination of packages and configurations. So we have a set of test cases that we run to make sure the key features are covered.

As much as we’d like to ship with zero bugs, if we waited until we reached that state, there’d never be another Fedora release again. Instead, we’ve defined release criteria that define what bugs can block the release. We have basic release criteria that apply to all release milestones, and then separate, cumulative criteria for beta and final releases. With beta releases, we’re generally a little more forgiving of rough edges. For a final release, it needs to pass all of beta’s criteria, plus some more that help make it a better user experience.

The week before a scheduled release, we hold a “go/no go meeting“. During this meeting, the QA team, release engineering team, and the Fedora Engineering Steering Committee (FESCo) decide whether or not we will ship the release. As part of the decision process, we conduct a final review of blocker bugs. If any accepted blockers remain, we push the release back to a later date.

Some bugs aren’t severe enough to block the release, but we still would like to get them fixed before the release. This is particularly true of bugs that affect the live image experience. In that case, we grant an exception for updates that fix those bugs.

How you can help

In all my years as a Fedora contributor, I’ve never heard the QA team say “we don’t need any more help.” Contributing to the pre-release testing processes can be a great way to make your first Fedora contribution.

The Blocker Review meetings happen most Mondays in #fedora-blocker-review on IRC. All members of the Fedora community are welcome to participate in the discussion and voting. One particularly useful contribution is to look at the proposed blockers and see if you can reproduce them. Knowing if a bug is widespread or not is important to the blocker decision.

In addition, the QA team conducts test days and test weeks focused on various parts of the distribution: the kernel, GNOME, etc. Test days are announced on Fedora Magazine.

There are plenty of other ways to contribute to the QA process. The Fedora wiki has a list of tasks and how to contact the QA team. The Fedora 32 Beta release is a few weeks away, so now’s a great time to get started!

Posted on Leave a comment

Make free encrypted backups to the cloud on Fedora

Most free cloud storage is limited to 5GB or less. Even Google Drive is limited to 15GB. While not heavily advertised, IBM offers free accounts with a whopping 25GB of cloud storage for free. This is not a limited time offer, and you don’t have to provide a credit card. It’s absolutely free! Better yet, since it’s S3 compatible, most of the S3 tools available for backups should work fine.

This article will show you how to use restic for encrypted backups onto this free storage. Please also refer to this previous Magazine article about installing and configuring restic. Let’s get started!

Creating your free IBM account and storage

Head over to the IBM cloud services site and follow the steps to sign up for a free account here: https://cloud.ibm.com/registration. You’ll need to verify your account from the email confirmation that IBM sends to you.

Then log in to your account to bring up your dashboard, at https://cloud.ibm.com/.

Click on the Create resource button.

Click on Storage and then Object Storage.

Next click on the Create Bucket button.

This brings up the Configure your resource section.

Next, click on the Create button to use the default settings.

Under Predefined buckets click on the Standard box:

A unique bucket name is automatically created, but it’s suggested that you change this.

In this example, the bucket name is changed to freecloudstorage.

Click on the Next button after choosing a bucket name:

Continue to click on the Next button until you get the the Summary page:

Scroll down to the Endpoints section.

The information in the Public section is the location of your bucket. This is what you need to specify in restic when you create your backups. In this example, the location is s3.us-south.cloud-object-storage.appdomain.cloud.

Making your credentials

The last thing that you need to do is create an access ID and secret key. To start, click on Service credentials.

Click on the New credential button.

Choose a name for your credential, make sure you check the Include HMAC Credential box and then click on the Add button. In this example I’m using the name resticbackup.

Click on View credentials.

The access_key_id and secret_access_key is what you are looking for. (For obvious reasons, the author’s details here are obscured.)

You will need to export these by calling them with the export alias in the shell, or putting them into a backup script.

Preparing a new repository

Restic refers to your backup as a repository, and can make backups to any bucket on your IBM cloud account. First, setup the following environment variables using your access_key_id and secret_access_key that you retrieved from your IBM cloud bucket. These can also be set in any backup script you may create.

$ export AWS_ACCESS_KEY_ID=<MY_ACCESS_KEY>
$ export AWS_SECRET_ACCESS_KEY=<MY_SECRET_ACCESS_KEY>

Even though you are using IBM Cloud and not AWS, as previously mentioned, IBM Cloud storage is S3 compatible, and restic uses its interal AWS commands for any S3 compatible storage. So these AWS keys really refer to the keys from your IBM bucket.

Create the repository by initializing it. A prompt appears for you to type a password for the repository. Do not lose this password because your data is irrecoverable without it!

restic -r s3:http://PUBLIC_ENDPOINT_LOCATION/BUCKET init

The PUBLIC_ENDPOINT_LOCATION was specified in the Endpoint section of your Bucket summary.

For example:

$ restic -r s3:http://s3.us-south.cloud-object-storage.appdomain.cloud/freecloudstorage init

Creating backups

Now it’s time to backup some data. Backups are called snapshots. Run the following command and enter the repository password when prompted.

restic -r s3:http://PUBLIC_ENDPOINT_LOCATION/BUCKET backup files_to_backup

For example:

$ restic -r s3:http://s3.us-south.cloud-object-storage.appdomain.cloud/freecloudstorage backup Documents/
Enter password for repository: repository 106a2eb4 opened successfully, password is correct Files: 51 new, 0 changed, 0 unmodified Dirs: 0 new, 0 changed, 0 unmodified Added to the repo: 11.451 MiB processed 51 files, 11.451 MiB in 0:06 snapshot 611e9577 saved

Restoring from backups

Now that you’ve backed up some files, it’s time to make sure you know how to restore them. To get a list of all of your backup snapshots, use this command:

restic -r s3:http://PUBLIC_ENDPOINT_LOCATION/BUCKET snapshots

For example:

$ restic -r s3:http://s3.us-south.cloud-object-storage.appdomain.cloud/freecloudstorage snapshots
Enter password for repository: ID Date Host Tags Directory ------------------------------------------------------------------- 106a2eb4 2020-01-15 15:20:42 client /home/curt/Documents

To restore an entire snapshot, run a command like this:

restic -r s3:http://s3.us-south.cloud-object-storage.appdomain.cloud/freecloudstorage restore snapshotID --target restoreDirectory

For example:

$ restic -r s3:http://s3.us-south.cloud-object-storage.appdomain.cloud/freecloudstorage restore 106a2eb4 --target ~
Enter password for repository: repository 106a2eb4 opened successfully, password is correct
restoring <Snapshot 106a2eb4 of [/home/curt/Documents]

If the directory still exists on your system, be sure to specify a different location for the restoreDirectory. For example:

restic -r s3:http://s3.us-south.cloud-object-storage.appdomain.cloud/freecloudstorage restore 106a2eb4 --target /tmp

To restore an individual file, run a command like this:

restic -r s3:http://PUBLIC_ENDPOINT_LOCATION/BUCKET restore snapshotID --target restoreDirectory --include filename

For example:

$ restic -r s3:http://s3.us-south.cloud-object-storage.appdomain.cloud/freecloudstorage restore 106a2eb4 --target /tmp --include file1.txt Enter password for repository: restoring <Snapshot 106a2eb4 of [/home/curt/Documents] at 2020-01-16 15:20:42.833131988 -0400 EDT by curt@client> to /tmp

Photo by Alex Machado on Unsplash.

[EDITORS NOTE: The Fedora Project is sponsored by Red Hat, which is owned by IBM.]

[EDITORS NOTE: Updated at 1647 UTC on 24 February 2020 to correct a broken link.]

Posted on Leave a comment

Demonstrating PERL with Tic-Tac-Toe, Part 1

Larry Wall’s Practical Extraction and Reporting Language (PERL) was originally developed in 1987 as a general-purpose Unix scripting language that borrowed features from C, sh, awk, sed, BASIC, and LISP. In the late 1990s, before PHP became more popular, PERL was commonly used for CGI scripting. PERL is still the go-to tool for many sysadmins who need something more powerful than sed or awk when writing complex parsing and automation scripts. It has a somewhat high learning curve due to its dense notation. But a recent survey indicates that PERL developers earn 54 per cent more than the average developer. So it may still be a worthwhile language to learn.

PERL is far too complex to cover in any significant detail in this magazine. But this short series of articles will attempt to demonstrate a few of the most basic features of the language so that you can get a sense of what the language is like and the kind of things it can do.

An example PERL program

PERL was originally a language optimized for scanning arbitrary text files, extracting information from those text files, and printing reports based on that information. To demonstrate how this core feature of PERL works, a very simple Tic-Tac-Toe game is provided below. The below program scans a textual representation of a Tic-Tac-Toe board, extracts and manipulates the numbers on the board, and prints the modified result to the console.

00 #!/usr/bin/perl
01 02 use feature 'state';
03 04 use constant MARKS=>[ 'X', 'O' ];
05 use constant BOARD=>'
06 ┌───┬───┬───┐
07 │ 1 │ 2 │ 3 │
08 ├───┼───┼───┤
09 │ 4 │ 5 │ 6 │
10 ├───┼───┼───┤
11 │ 7 │ 8 │ 9 │
12 └───┴───┴───┘
13 ';
14 15 sub get_mark {
16 my $game = shift;
17 my @nums = $game =~ /[1-9]/g;
18 my $indx = (@nums+1) % 2;
19 20 return MARKS->[$indx];
21 }
22 23 sub put_mark {
24 my $game = shift;
25 my $mark = shift;
26 my $move = shift;
27 28 $game =~ s/$move/$mark/;
29 30 return $game;
31 }
32 33 sub get_move {
34 return (<> =~ /^[1-9]$/) ? $& : '0';
35 }
36 37 PROMPT: {
38 state $game = BOARD;
39 40 my $mark;
41 my $move;
42 43 print $game;
44 45 last PROMPT if ($game !~ /[1-9]/);
46 47 $mark = get_mark $game;
48 print "$mark\'s move?: ";
49 50 $move = get_move;
51 $game = put_mark $game, $mark, $move;
52 53 redo PROMPT;
54 }

To try out the above program on your PC, you can copy-and-paste the above text into a plain text file and save and run it. The line numbers will have to be removed before the program will work. Of course, the command that one uses to perform that sort of textual extraction and reporting is perl.

Assuming that you have saved the above text to a file named game.txt, the following command can be used to strip the leading numbers from all the lines and write the modified version to a new file named game:

$ cat game.txt | perl -npe 's/...//' > game

The above command is a very small PERL script and it is an example of what is called a one-liner.

Now that the line numbers have been removed, the program can be run by entering the following command:

$ perl game

How it works

PERL is a procedural programming language. A program written in PERL consists of a series of commands that are executed sequentially. With few exceptions, most commands alter the state of the computer’s memory in some way.

Line 00 in the Tic-Tac-Toe program isn’t technically part of the PERL program and it can be omitted. It is called a shebang (the letter e is pronounced soft as it is in the word shell). The purpose of the shebang line is to tell the operating system what interpreter the remaining text should be processed with if one isn’t specified on the command line.

Line 02 isn’t strictly necessary for this program either. It makes available an advanced command named state. The state command creates a variable that can retain its value after it has gone out of scope. I’m using it here as a way to avoid declaring a global variable. It is considered good practice in computer programming to avoid using global variables where possible because they allow for action at a distance. If you didn’t follow all of that, don’t worry about it. It’s not important at this point.

PERL scopes, blocks and subroutines

Scope is a very important concept that one needs to be familiar with when reading and writing procedural programs. In PERL, scope is often delineated by a pair of curly brackets. Within the global scope, the above Tic-Tac-Toe program defines four sub-scopes on lines 15-21, 23-31, 33-35 and 37-54. The first three scopes are prefixed with subroutine declarations and the last scope is prefixed with the label PROMPT.

Scopes serve multiple purposes in programming languages. One purpose of a scope is to group a set of commands together as a unit so that they can be called repeatedly with a single command rather than having to repeat several lines of code each time in the program. Another purpose is to enhance the readability of the program by denoting a restricted area where the value of a variable can be updated.

Within the scope that is labeled PROMPT and defined on lines 37-54 of the above Tic-Tac-Toe program, a variable named mark is created using the my keyword (line 40). After it is created, it is assigned a value by calling the get_mark subroutine (line 47). Later, the put_mark subroutine is called (line 51) to change the value in the square that was chosen by the get_move subroutine on line 50.

Hopefully it is obvious that the mark that put_mark is setting is meant to be the same mark that get_mark retrieved earlier. As a programmer though, how do I know that the value of mark wasn’t changed when the get_move subroutine was called? This example program is small enough that every line can be examined to make that determination. But most programs are much larger than this example and having to know exactly what is going on at all points in the program’s execution can be overwhelming and error-prone. Because mark was created with the my keyword, its value can only be accessed and modified within the scope that it was created (or a sub-scope). It doesn’t matter what subroutines at parallel or higher scopes do; even if they change variables with the same name in their own scopes. This property of scopes — restricting the range of lines on which the value of a variable can be updated — improves the readability of the code by allowing the programmer to focus on a smaller section of the program without having to be concerned about what is happening elsewhere in the program.

Lines 04 and 05 define the MARKS and BOARD variables, respectively. Because they are not within any curly bracket pairing, they exist in the global scope. It is permissible to create constant variables in the global scope because they are read-only and therefore not subject to the action at a distance concern. In PERL, it is traditional to name constants in all upper case letters.

Notice that scopes can be nested such that variables defined in outer scopes can be accessed and modified from within inner scopes. This is why the MARKS and BOARD variables can be accessed within the get_mark subroutine and PROMPT block respectively — they are sub-scopes of the global scope.

The statements in the program are executed in order from top to bottom and left to right. Each statement is terminated with a semi-colon (;). The semi-colon can be omitted from the last statement in any scope and from after the last block of many statements that define the flow of the program such as sub, if and while.

In PERL nomenclature, scopes are called blocks. Scope is the more general term that is typically used in online references like Wikipedia, but the remainder of this article will use the more perlish term blocks.

The statements within the first three blocks are not immediately executed as the program is evaluated from top to bottom. Rather, they are associated with the subroutine name preceding the block. This is the function of the sub keyword — it associates a subroutine name with a block of statements so that they can be called as a unit elsewhere in the program. The three subroutines get_mark (lines 15-21), put_mark (lines 23-31), and get_move (lines 33-35) are called on lines 47, 51 and 50 respectively.

The PROMPT block is not associated with a subroutine definition or other flow-control statement, so the statements within it are immediately executed in sequence when the program is run.

PERL regular expressions

If there is one feature that is more central to PERL than any other it is regular expressions. Notice that in the example Tic-Tac-Toe program every block contains a =~ (or !~) operator followed by some text surrounded with forward slashes (/). The text within the forward slashes is called a regular expression and the operator binds the regular expression to a variable or data stream.

It is important to note that there are different regular expression syntaxes. Some editors and command-line tools (for example, grep) allow the user to select which regular expression syntax they prefer to use. PERL-Compatible Regular Expressions (PCRE) are by far the most powerful.

Regular expressions used in matching operations

The result of applying the regular expression to a variable or data stream is usually a value that, when used in a flow-control statement such as if or while, will evaluate to true or false depending on whether or not the match succeeded. There are modifiers that can be appended to the closing slash of a regular expression to change its return value.

Line 45 of the Tic-Tac-Toe program provides a typical example of how a regular expression is used in a PERL program. The regular expression [1-9] is being applied to the variable game which holds the in-memory representation of the Tic-Tac-Toe game board. The expression is a character class that matches any character in the range from 1 to 9 (inclusive). The result of the regular expression will be true only if a character from 1 to 9 is present in what is being evaluated. On line 45, the !~ operator applies the regular expression to the game variable and negates its sense such that the result will be true only if none of the characters from 1 to 9 are present. Because the regular expression is embedded within the conditional clause of the if statement modifier, the statement last PROMPT is only executed if there are no characters in the range from 1 to 9 left on the game board.

The last statement is one of a few flow-control statements in PERL that allow the program execution sequence to jump from the current line to another line somewhere else in the program. Other flow-control statements that work in a similar fashion include next, continue, redo and goto (the goto statement should be avoided whenever possible because it allows for spaghetti code).

In the example Tic-Tac-Toe program, the last PROMPT statement on line 45 causes program execution to resume just after the PROMPT block. Because there are no more statements in the program, the program will terminate.

The label PROMPT was chosen arbitrarily. Any label (or none at all) could have been used.

The redo PROMPT statement at the end of the PROMPT block causes program execution to jump back to the beginning of the PROMPT block.

Notice that the state keyword like the my keyword creates a variable that can only be accessed or modified within the block that it is created (or a nested sub-block if any exist). Unlike the my keyword, variables created with the state keyword keep their former value when the blocks they are in are called repeatedly. This is the behavior that is needed for the game variable because it is being updated incrementally each time the PROMPT block is run. The mark and move variables are meant to be different on each iteration of the PROMPT block, so they do not need to be created with the state keyword.

Regular expressions used for input validation

Another common use of regular expressions is for input validation. Line 34 of the example Tic-Tac-Toe program provides an example of a regular expression being used for input validation. The expression on line 34 is similar to the one on line 45. It is also checking for characters from 1 to 9. However, it is performing the check against the null filehandle (<>); it is using the =~ operator; and it is prefixed and suffixed with the zero-width assertions ^ and $ respectively.

The null filehandle, when accessed as it is on line 34, will cause the program to pause until one line of input is provided. The regular expression will evaluate to true only if the line contains one character in the range from 1 to 9. The assertions ^ and $ do not match any characters. Rather, they match the beginning and end positions, respectively, on the line. The regular expression effectively reads: “Begin (^) with one character in the range from 1 to 9 ([1-9]) and end ($)”.

Because it is embedded in the conditional clause of the ternary operator, line 34 will return either what was matched ($&) if the match succeeded or the character zero (0) if it failed. If the input were not validated in this way, then the user could submit their opponent’s mark rather than a number on the board.

Regular expressions used for filtering data

Line 17 demonstrates using the global modifier (g) on a regular expression. With the global modifier, the regular expression will return the number of matches instead of true or false. In list context, it returns a list of all the matched substrings.

Line 17 uses a regular expression to copy all the numbers in the range from 1 to 9 from the game variable into the array named nums. Line 18 then uses the modulo operator with the integer 2 as its second argument to determine whether the length of the nums array is even or odd. The formula on line 18 will result in 0 if the length of nums is odd and 1 if the length of nums is even. Finally, the computed index (indx) is used to access an element of the MARKS array and return it. Using this formula, the get_mark function will alternately return X or O depending on whether there are an odd or even number of positions left on the board.

Regular expressions used for substituting data

Line 28 demonstrates yet another common use of regular expressions in PERL. Rather than being used in a match operator (m), the regular expression on line 28 is being used in a substitution operator (s). If the value in the move variable is found in the game variable, it will be substituted with the value of the mark variable.

PERL sigils and data types

The last things of note that are used in the example Tic-Tac-Toe program are the sigils ($ and @) that are placed before the variable names. When creating a variable, the sigil indicates the type of variable being created. It is important to note that a different sigil can be prefixed to the variable name when it is accessed to indicate whether one or many items should be returned from the variable.

There are three built-in data types in PERL: scalars ($), arrays (@) and associative arrays (%). Scalars hold a single data item such as a number, character or string of characters. Arrays are numerically indexed sets of scalars. Associative arrays are arrays that are indexed by scalars rather than numbers.