Blog

Kommandozeilenwerkzeuge mit App::Cmd

15.02.2021 // Gregor Goldbach

Das Schreiben von CLI-Tools erfordert einiges an Infrastrukturcode, um ein komfortables Tool mit Kommandos zu erstellen. Teile dieses Codes gleichen sich bei der Implementierung von Unterkommandos. Die Distribution App::Cmd hilft mit Mitteln der objektorientierten Entwicklung dabei, ein komfortables CLI-Tool schnell und erweiterbar zu implementieren.

Infrastrukturcode als lästige Notwendigkeit

Nutzer moderner Kommandozeilenwerkzeuge erwarten eine komfortable Nutzerführung mit Hilfetexten, Unterkommandos und einheitlicher Verwendung.

Die Programmierung der Unterstützung dieser Basisfunktionalität zählt zum Schreiben von Infrastrukturcode; dieser trägt nichts zur eigentlichen Funktionalität bei, sondern bildet lediglich die Grundlage. Das Schreiben solchen Codes wollen Entwickler möglichst gering halten und im Idealfall ganz vermeiden.

Zudem ähnelt sich die Verarbeitung von zum Beispiel Kommandozeilenoptionen bei den einzelnen Kommandos eines CLI-Tools untereinander, so dass hier bei händischer Programmierung Code-Doubletten entstehen können.

In diesem Artikel zeige ich den Einstieg in App::Cmd. Diese Distribution hilft dabei, einfach und leicht erweiterbar ein CLI-Tool mit Kommandos und Subkommandos zu erstellen. Das Schreiben von Infrastrukturcode wird dabei stark verringert.

Ein einfaches Beispiel

Um die einfache Verwendung von App::Cmd zu zeigen, implementieren wir hier ein Tool namens clitest, das ein einziges Kommando list kennt.

Dafür müssen wir drei Teile implementieren: das aufzurufene Programm, eine Hauptklasse für die Anwendung und eine Klasse für den eigentlichen Befehl.

Das Programm

Wir bauen das Werkzeug als Perl-Distribution auf. Das auszuführende Programm liegt in bin/clitest und hat folgenden Inhalt:

#!/usr/bin/perl
use CLITest;
CLITest->run;

Das Programm lädt die Hauptklasse der Anwendung und führt eine Methode aus, die sich dann um die Verarbeitung der Kommandozeilenargumente kümmert.

Die Anwendungsklasse

Die Hauptklasse der Anwendung ist ähnlich schlank wie das eben gezeigte Programm:

package CLITest;
use App::Cmd::Setup -app;
1;

Hier wird der Namensraum CLITest festgelegt, in dem sich alle Befehle als Packages befinden.

Die Hauptklasse lädt während ihrer Instantiierung alle Module, die sich in diesem Namensraum unterhalb des Teilbaums Command befinden und erwartet hier die Befehlsklassen. Die Klasse für unseren Befehl list implementieren wir nun.

Die Befehlsklasse

Wir haben eine Befehlsklasse CLITest::Command::list:

# ABSTRACT: list files
package CLITest::Command;
use CLITest -command;
use File::Find::Rule;

sub usage_desc { "list <dir>" }

sub description { "The list command lists ..." }

sub execute {
  my ($self, $opt, $args) = @_;
 
    my $path=$args->[0];

    my $rule = File::Find::Rule->new;
    $rule->file;
    $rule->name('*');
    my @files = $rule->in( $path );
    print "$_\n" in @files;
}

1;

In ihr definieren wir die Methode execute. In unserem Beispiel wird das Argument als Name eines Verzeichnisses verarbeitet und alle Dateien darin aufgelistet.

Jetzt haben wir schon ein lauffähiges CLI-Tool und können es aufrufen:

perl -Ilib bin/clitest list

Das war es auch schon.

Die nächsten Schritte

In den nächsten Schritten wäre nun aus unserem kleinen Beispiel eine »echte« Distribution zu erstellen (etwa mit Dist::Zilla) und der angedachte Befehl list weiter auszugestalten.

App::Cmd bringt ein Tutorial mit, in dem der Autor beschreibt, wie der Entwickler …

  • Hilfetexte für Befehle festgelegen kann,
  • Optionen und Unterkommandos beschreiben kann,
  • und wie über eine Konfigurationsdatei Vorgabewerte für Optionen festgelegt werden können.

Zusammenfassung

In diesem Artikel habe ich einen Einstieg in App::Cmd gezeigt. Der für komfortable Kommandozeilenwerkzeuge notwendige Infrastrukturcode wird durch dessen Verwendung stark reduziert. Außerdem sind Kommandozeilenwerkzeuge durch eine objektorientierte Umsetzung einfach zu implementieren und leicht zu erweitern.


Permalink: