Aus Linux-Magazin 06/2012

PHP für die Kommandozeile

© Jamalludin Abu Seman, 123RF

Die Sprache PHP taugt nicht nur für bunte Webseiten: Seit zehn Jahren lassen sich die Skripte auch auf der Befehlszeile nutzen. Damit passt die Skriptsprache mit ihren umfangreichen Bibliotheken besonders gut in den Werkzeugkasten von Administratoren, die Webserver verwalten.

Die Skriptsprache PHP ist nahezu untrennbar mit Webanwendungen verbunden. Dabei erledigt PHP auch wiederkehrende, aufwändige oder einfach nur lästige Aufgaben auf der Kommandozeile. Die passende Komponente enthält die Sprache seit Version 4.3 aus dem Jahr 2002. Der Command Line Interpreter oder kurz PHP-CLI [1] steckt bei den meisten Distributionen in einem separaten Paket. In der Regel heißt es wie der Interpreter selbst, bei Debian 6 (Squeeze) und Ubuntu etwa »php5-cli« . Unter Open Suse 12.1 genügt hingegen das Komplettpaket »php5« .

Heraus damit

Der CLI verdaut alle normalen PHP-Skripte. Ihre Ausgaben landen erwartungsgemäß nicht in einem Browser, sondern in der Standardausgabe. Ein einfaches Hallo-Welt-Programm sieht folglich wie in Listing 1 aus. Dank der ersten Zeile (mit dem Shebang) kann der Anwender die Datei als ausführbar kennzeichnen und wie ein normales Shellskript aufrufen. Andernfalls startet er es mit »php hallowelt.php« . Das angehängte »\n« hinter »Hallo Welt« sorgt in der Ausgabe für einen Zeilenumbruch, »exit(0);« wirft schließlich noch einen passenden Exit-Code aus.

Listing 1

Hallo-Welt-Programm

01 #!/usr/bin/php
02 <?php
03         echo "Hallo Welt\n";
04         exit(0);
05 ?>

Bei den ersten Gehversuchen mit dem Interpreter sollte sich der Admin versichern, dass er auch wirklich die CLI-Variante aufruft, denn auf Webservern könnte auch das CGI-PHP die Skripte ausführen. Der Kasten “PHP als CLI und CGI” erläutert Näheres.

PHP als CLI und CGI

Den PHP-Interpreter gibt es nicht nur für die Kommandozeile, sondern auch in einer Variante für die CGI-Schnittstelle. Diese nutzen Webserver, um externe Programme zu starten. Die CLI- unterscheidet sich von der CGI-Fassung vor allem in ihren Ausgaben. So liefert der CGI-Interpreter immer auch einen HTTP-Header mit, die Ausgabe von Listing 1 sähe also beispielsweise so aus:

X-Powered-By: PHP/5.3.6-13ubuntu3.6
Content-type: text/html
Hallo Welt!

Diesen Vorspann braucht der Programmierer auf der Kommandozeile nicht. Mit einer einfachen »if« -Abfrage testet der Anwender, ob das Skript wie gewünscht unter der Kommandozeilenvariante läuft:

if (PHP_SAPI === 'cli')
{
        [...]
}

Mit diesem Test stellt er nebenbei auch sicher, dass nicht ein Webserver das Skript (versehentlich) startet.

Ob man an der Kommandozeile die CLI-Fassung aufruft, verrät zudem das Kommando »php -v« . In der Ausgabe müsste irgendwo der Hinweis »(CLI)« auftauchen.

Selbst geschriebene Skripte klopft vor dem ersten Start

php -l Skript.php

auf Tipp- und Syntaxfehler ab. Selbstverständlich lässt sich der Interpreter auch über die Standardeingabe mit PHP-Befehlen füttern, der Benutzer darf sie folglich über Pipes mit anderen Programmen verbinden:

echo "<?php echo 'Hallo'; ?>" | php> gruss.txt

In der textorientierten Welt der Kommandozeile muss der Sysadmin häufig mit Zeichenketten (Strings) jonglieren und sie umbauen. Das Zusammenfügen von mehreren Strings geht in PHP angenehm leicht von der Hand:

$datei = "urlaub";
$endung = "png";
echo "/home/tim/" . $datei . ".$endung \n";

Variablen kennzeichnet in PHP immer ein vorangestelltes Dollarzeichen, was Bash-Programmierern bekannt vorkommen dürfte. Die Punkte außerhalb der Anführungszeichen hängen die Zeichenketten aneinander, Variablen innerhalb der Anführungszeichen ersetzt PHP automatisch durch ihren Inhalt.

Anleihen bei C

Für komplexere Operationen stehen Funktionen bereit, die sich an ihre Pendants aus der Programmiersprache C anlehnen. So liefert etwa »strlen($name);« die Länge der Zeichenkette in »$name« zurück, während »printf()« einen String mit Hilfe der ebenfalls aus C bekannten Platzhalter zusammenbaut:

$anzahl=5;
printf("Sie haben %d Nachrichten \n",$anzahl);

Mehrere Wege gibt es, um Strings auseinanderzupflücken. So zerschneidet »explode()« einen String anhand einer vorgegebenen Zeichenkette und packt die Teile in ein Array. Listing 2 zersägt beispielsweise den Text »Hallo Herr Meier« an den Leerzeichen und speichert die Einzelteile in einem Array. Genau dieses durchläuft anschließend die »foreach()« -Schleife und gibt seine Inhalte zeilenweise aus. Arrays sind in PHP übrigens das, was andere Sprachen als Dictionarys oder Hashtables bezeichnen. Diese Datenstrukturen speichern einen Wert unter einem Schlüssel ab, im Beispiel findet man unter dem Schlüssel »« das erste abgetrennte Wort:

Listing 2

explode()

01 <?php
02         $text  = "Hallo Herr Maier";
03         $teile = explode(" ", $text);
04
05         foreach ($teile as $nr => $teil) {
06                 echo "$teil\n";
07         }
08 ?>\
echo $teile[0];

Außerdem darf der Programmierer Teilstrings auch mittels regulärer Ausdrücke im Perl-Stil suchen:

preg_match("/Herr/", "Hallo Herr Maier",$treffer);
echo $treffer[0];

Im Array »$treffer« legt »preg_match()« alle Fundstellen ab, im Beispiel enthält »$treffer[0]« die Zeichenkette »Herr« .

Handfeste Argumente

Vorsicht ist geboten, wenn man an den CLI fertige PHP-Skripte verfüttert, die eigentlich für das Web bestimmt sind. Zunächst einmal gibt der CLI einfach alle Texte aus, die um »<?php … ?>« herumstehen. Im Extremfall sieht der Anwender also ellenlangen HTML-Code auf der Kommandozeile durchlaufen.

Des Weiteren fehlen die Arrays »$_GET« , »$_POST« und »$_COOKIE« , in denen sonst die vom Browser übergebenen Daten landen. Stattdessen stecken alle auf der Kommandozeile übergebenen Argumente im Array »$_SERVER[‘argv’]« . Wie viele Argumente übergeben wurden, verrät die Variable »$_SERVER[‘argc’]« . Ein Beispiel für ihre Verwendung zeigt Listing 3, das einfach alle übergebenen Argumente auflistet. Wie im Ergebnis in Abbildung 1 zu sehen, ist das erste Argument immer der Dateiname des ausgeführten Skripts selbst.

Listing 3

Argumente auswerten

01 <?php
02         echo "Anzahl Argumente: " . $_SERVER['argc'] . "\n";
03
04         $argumente = $_SERVER['argv'];
05         foreach ($argumente as $nr => $argument) {
06                 echo "argumente[$nr] = $argument\n";
07         }
08
09         exit(0);
10  ?>
Abbildung 1: Die dem Programm übergebenen Argumente lassen sich auch in PHP-Skripten auswerten.

Abbildung 1: Die dem Programm übergebenen Argumente lassen sich auch in PHP-Skripten auswerten.

Abbildung 2: Die Funktion »posix_getpwuid()« liefert einige Informationen zu dem Benutzer, unter dem das Skript läuft.

Abbildung 2: Die Funktion »posix_getpwuid()« liefert einige Informationen zu dem Benutzer, unter dem das Skript läuft.

Auf eine kleines Problem trifft der Benutzer, wenn er ein Skript per »php« aufruft und ihm gleichzeitig noch Argumente übergeben möchte, die mit einem »-« beginnen:

php beispiel.php -h

In diesem Fall würde der CLI selbst das »-h« interpretieren und seinen eigenen Hilfetext runterbeten. Abhilfe schafft das Argument-Trennzeichen »–« :

php beispiel.php -- -h

Dieses Problem tritt nicht auf, wenn der Anwender das Skript wie in Listing 1 mit einem Shebang versieht, ausführbar macht und dann direkt aufruft.

Konservenöffner

Dateien öffnet PHP wie viele andere Programmiersprachen mit der Funktion »fopen()« , die ein so genanntes Handle zurückgibt. Diesen Zeiger auf die Datei benötigen dann alle weiteren Ein- und Ausgaberoutinen. Dazu zählt beispielsweise »fgets()« , das eine Textzeile aus der Datei einliest:

$handle = fopen("test.txt", "r");
$zeile = trim(fgets($handle));
fclose($handle);

Die Funktion »trim()« entfernt hier zusätzlich noch alle Whitespaces – also Leerzeichen und Tabulatoren – am Anfang und Ende einer jeden Zeile. Das ordnungsgemäße Schließen der Datei erledigt »fclose()« . Das Programmiererleben erleichtern ein paar vordefinierte Konstanten. Statt erst die Standardeingabe zu öffnen mit

fopen("php://stdin", "r");

und dann eine Zeile zu lesen, kann der PHP-Anwender auch direkt zu »STDIN« greifen:

$zeile = fgets(STDIN);

Analog gibt es noch »STDOUT« und »STDERR« für die Standardausgabe beziehungsweise Standardfehlerausgabe. Auch das zeilenweise Einlesen einer Datei gerät mit PHP zum Kinderspiel, wie Listing 4 beweist. In seiner zweiten Zeile öffnet »file()« eine Datei und liefert alle Zeilen in einem Array zurück. Dieses durchläuft anschließend die »foreach« -Schleife und gibt jede Zeile auf dem Bildschirm aus.

Listing 4

Zeilenweises Einlesen einer Datei

01 <?php
02
03         $dateiname ="test.txt";
04         $zeilen = file($dateiname);
05
06         foreach ( $zeilen as $nr => $zeile ) {
07                 $zeile = trim($zeile);
08                 echo "$nr : $zeile\n";
09         }
10
11         exit(0);
12 ?>

Praktisch angewandt

Für die Programmieraufgabe aus der Einführung zu dieser Titelstrecke hält PHP selbstverständlich auch passende Funktionen bereit. Das Einlesen der CSV-Datei lässt sich mit »fgetcsv()« erledigen, Beispielcode zeigt Listing 5. Zunächst öffnet »fopen()« die CSV-Datei »liste.csv« . »fgetcsv()« holt aus ihr die nächste Zeile, extrahiert die einzelnen Datenfelder und legt diese dann umgehend in das Array »$data« . Die nachfolgende »foreach()« -Schleife gibt alle Elemente im Array der Reihe nach aus.

Listing 5

CSV-Datei einlesen

01 <?php
02         $datei = fopen("liste.csv", "r");
03         while (($data = fgetcsv($datei, 0, ",")) !== FALSE) {
04
05                 echo "Zeileninhalt:\n";
06                 foreach ( $data as $nr => $eintrag ) {
07                         $eintrag = trim($eintrag);
08                         echo "$eintrag\n";
09                 }
10
11         }
12         fclose($datei);
13         exit(0);
14 ?>

Aufgrund der »while()« -Schleife wiederholt sich diese Prozedur so lange, bis »fgetcvs()« das Ende der CSV-Datei erreicht hat und somit keine weitere Zeile zurückliefern kann. Viel komfortabler und kürzer geht es kaum. Der dritte Parameter von »fgetcsv()« gibt das Trennzeichen vor, in Listing 5 trennt ein Komma die Einträge. Tauscht der Admin es gegen einen Doppelpunkt, kann er ebenso einfach die Systemdatei »/etc/passwd« einlesen:

$datei = fopen("/etc/passwd", "r");
$data = fgetcsv($datei, 0, ":");

Das Gegenstück zu »fgetcsv()« heißt »fputcsv()« . Sie legt in CSV-Dateien halb-automatisch eine passende Zeile ab. Dazu übergibt der Programmierer der Funktion lediglich das gewünschte Trennzeichen sowie ein Array, in dem sich alle Daten für eine Zeile befinden:

$datensatz = array("tims", "Tim","Schürmann");
$datei = fopen("test.csv", "w");
fputcsv($datei, $datensatz, ",");

Auf der Kommandozeile erweisen sich vor allem die zahlreichen Posix-Funktionen als nützlich. So liefert etwa »posix_geteuid()« die Benutzer-ID zurück, unter der das Skript gerade läuft. Die kann man dann wiederum an »posix_geteuid()« verfüttern, was ein Array mit allen Benutzerinformationen ausspuckt. In Listing 6 ist dafür ein Beispiel zu sehen, die Ausgaben finden sich in Abbildung 2.

Listing 6

Prozesseigenschaften

01 <?php
02     $uid = posix_geteuid();
03     $benutzer = posix_getpwuid($uid);
04     echo "Benutzer des Prozesses: $benutzer[name] \n";
05     echo "UID: $benutzer[uid] \n";
06     echo "GID: $benutzer[gid] \n";
07     echo "Heimatverzeichnis: $benutzer[dir] \n";
08 ?>

Die Zugriffsrechte auf eine Datei ermittelt »posix_access()« . Listing 7 zeigt die Funktion in der Praxis, Abbildung 3 ein paar Beispielausgaben.

Listing 7

Test auf Dateirechte mit posix_access()

01 <?php
02     $existiert = posix_access("/etc/passwd", POSIX_F_OK);
03     if($existiert) echo "Datei existiert\n";
04
05     $lesen = posix_access("/etc/passwd", POSIX_R_OK);
06     if($lesen) echo "Darf Datei lesen\n";
07
08     $schreiben = posix_access("/etc/passwd", POSIX_W_OK);
09         if($schreiben) echo "Darf in Datei schreiben\n";
10
11     $ex = posix_access("/etc/passwd", POSIX_X_OK);
12     if($ex) echo "Darf die Datei ausführen\n";
13 ?>
Abbildung 3: Das Skript aus <link href="#article_l7" class="listing" srcset=

Listing 7, einmal als normaler Benutzer und einmal als Root aufgerufen.” width=”300″ height=”133″ /> Abbildung 3: Das Skript aus Listing 7, einmal als normaler Benutzer und einmal als Root aufgerufen.

Mit Dateien hantieren

Für fast jede Dateisystemoperation besitzt PHP eine entsprechende Funktion, teilweise sogar mehrere. So ändert »chmod()« die Zugriffsrechte einer Datei, »ls_dir()« prüft, ob der übergebene Dateiname ein Verzeichnis ist, »copy()« kopiert eine Datei und »mkdir()« legt ein neues Verzeichnis an:

mkdir("/home/tim/neu", 0700);

Das zweite Argument gibt die Zugriffsrechte an, in diesem Beispiel darf nur auf das Verzeichnis zugreifen, wer das Skript gestartet hat. Mit »scandir()« ist es zudem ein Leichtes, alle Dateien in einem Verzeichnis zu verarbeiten. Listing 8 gibt mit dieser Funktion alle Dateien des Unterverzeichnisses »Bilder« aus. Abbildung 4 zeigt die Funktion »getcwd()« .

Listing 8

scandir() im Einsatz

01 <?php
02         $dateien = scandir('Bilder');
03         foreach ($dateien as $datei) {
04                 echo "$datei \n";
05         };
06 ?>
Abbildung 4: Die Funktion »getcwd()« ermittelt das aktuelle Verzeichnis. Nach dem Start des Skripts ist dies genau das Verzeichnis, in dem der Anwender den Kommandozeilen-Interpreter aufgerufen hat.

Abbildung 4: Die Funktion »getcwd()« ermittelt das aktuelle Verzeichnis. Nach dem Start des Skripts ist dies genau das Verzeichnis, in dem der Anwender den Kommandozeilen-Interpreter aufgerufen hat.

Externe Programme startet die Exec-Funktion beispielsweise in der Form »exec(“gzip test.txt”)« . Praktischerweise kann der Admin im CLI fast alle PHP-Erweiterungen nutzen. So greift er etwa auf das erstellte Gzip-Archiv mit den entsprechenden Zlib-Funktionen zu. Beispielsweise schickt er dessen Inhalt auf die Konsole:

readgzfile("test.txt.gz");

Analog existieren noch weitere Funktionen, um etwa Zip- und Rar-Archive zu öffnen und zu manipulieren.

Mit den Kryptofunktionen generiert der Anwender schnell den eindeutigen Fingerabdruck einer Datei, im folgenden Beispiel mit dem MD5-Hashverfahren:

echo hash_file("md5", "test.txt");

Diese Funktionen sind nur ein kleiner Ausschnitt aus dem riesigen Angebot, das selbst exotische Wünsche befriedigen dürfte [2]. Es gibt sogar die Möglichkeit, über Ncurses-Funktionen eine kleine Benutzeroberfläche zu basteln.

Fazit

Auch wenn kaum jemand im ersten Moment an sie denkt, entpuppt sich PHP als eine nahezu perfekte Skriptsprache für die Kommandozeile. Dank der riesigen Funktionsbibliothek schrumpfen selbst umfangreiche Aufgaben auf wenige Zeilen Code zusammen, die zudem noch übersichtlich und gut lesbar sind.

Der CLI bietet auch einen interaktiven Modus, den der Befehl »php -a« aktiviert. Er kennt sogar eine Autovervollständigung: Ein Druck auf [Tab] genügt, und der nur teilweise eingetippte Befehl erscheint vollständig. Insbesondere wenn auf einem Rechner bereits PHP installiert ist, kann der Administrator also die Bash ruhig links liegen lassen und zu PHP-CLI greifen. (mhu)

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 4 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben