Die Interaktion mit externen Programmen ist eine entscheidende Eigenschaft von Perl, die seit der Schaffung der Sprache im Jahr 1987 für Aufgaben der Systemadministration und Automatisierung von Shell-Routinen geerbt wurde. Perl bietet mehrere Möglichkeiten, um einen externen Befehl auszuführen: den system-Operator, umgekehrte Backticks (``) oder qx//, die open-Funktion mit Pipe und modulare Wrapper wie IPC::Open3.
Das Hauptproblem beim Starten externer Prozesse besteht darin, die Ausgabe (stdout und stderr) korrekt zu erfassen, Fehlermeldungen beim Start zu verarbeiten, die Sicherheit von Parametern (um Injektionen zu vermeiden) sowie den Unterschied zwischen synchroner und asynchroner Ausführung.
Die Lösung besteht darin, die richtige Methode für die jeweilige Aufgabe zu wählen. Für einfache Befehle werden system oder umgekehrte Backticks verwendet, für komplexere Fälle die IPC::*-Module:
Beispielcode (Auslesen der Ausgaben eines Befehls und Fehlerbehandlung):
my $command = 'ls -l /tmp'; my $output = qx{$command}; if ($? == -1) { die "Fehler beim Start: $!"; } elsif ($? & 127) { warn sprintf "Befehl endete mit Signal %d", ($? & 127); } else { print "Ausgabe: $output"; }
Wichtige Besonderheiten:
Was unterscheidet system von exec in Perl?
Antwort: system führt den Befehl in einem externen Prozess aus und gibt die Kontrolle an Perl zurück, nachdem der Befehl beendet ist, während exec den aktuellen Perl-Prozess vollständig durch das ausgeführte Programm ersetzt, der nachfolgende Perl-Code wird nicht ausgeführt.
Beispiel:
system('echo Hallo'); exec('ls', '-l'); # Alles, das Perl-Skript wurde durch ls ersetzt, Perl läuft nicht weiter
Kann man Benutzervariablen sicher in Shell-Befehlen verwenden?
Antwort: Nur wenn man eine Argumentliste (nicht einen String) verwendet und die Interpolation in die Shell vermeidet. Andernfalls ist eine Befehlsinjektion möglich.
# Sicher: system("ls", "-l", $user_supplied_dir); # Gefährlich: system("ls -l $user_supplied_dir");
Wie erhält man sowohl stdout als auch stderr von einem externen Befehl?
Antwort: Ein zuverlässiger Weg ist die Verwendung von IPC::Open3 oder die Umleitung von stderr zu stdout auf der Shell-Ebene:
my $out = qx{ls /notexists 2>&1};
oder über IPC::Open3 (eine universellere und feinere Methode).
Ein Admin setzt über system("rm -rf $dir") einen vom Benutzer eingegebenen Wert ein.
Vorteile:
Nachteile:
Es wird system('rm', '-rf', $dir) verwendet, $dir wird validiert, es gibt ein Logging.
Vorteile:
Nachteile: