W3docs

popen()

Die Funktion popen() in PHP führt Shell-Befehle aus und ermöglicht die Interaktion über eine Pipe — ideal für das schrittweise Lesen großer Ausgaben.

Einführung

Die Funktion popen() führt einen Shell-Befehl in einem separaten Kindprozess aus und öffnet eine Pipe zu ihm — einen einseitigen Datenstrom, aus dem Sie die Ausgabe des Befehls lesen oder in den Sie Eingaben schreiben können. Sie ist PHPs Werkzeug, um Daten zeilenweise zu einem externen Programm zu streamen oder von dort zu empfangen, anstatt zu warten, bis der gesamte Befehl abgeschlossen ist.

Diese Seite behandelt, was popen() zurückgibt, die Modi r und w, warum sie immer mit pclose() kombiniert werden muss, wie sie sich von exec() und shell_exec() unterscheidet, und welche Sicherheitsregeln gelten, bevor Benutzereingaben an eine Shell übergeben werden.

Wann popen() verwendet werden sollte

Greifen Sie auf popen() zurück, wenn Sie einen Datenstrom benötigen, kein einmaliges Ergebnis:

  • Lesemodus ("r") — die Ausgabe eines Befehls schrittweise verarbeiten, z. B. ein Protokoll verfolgen, eine große find-Ausgabe lesen oder Zeilen aus einer Datenbank-CLI pipen, ohne alles im Speicher zu puffern.
  • Schreibmodus ("w") — Daten in die Standardeingabe eines Befehls einspeisen, z. B. Text in gzip, mail oder einen benutzerdefinierten Filter pipen.

Wenn Sie einfach die vollständige Ausgabe eines Befehls als string benötigen, ist shell_exec() oder exec() einfacher. Wenn Sie denselben Prozess gleichzeitig lesen und schreiben müssen, verwenden Sie proc_open()popen()-Pipes sind einseitig.

Syntax

popen(string $command, string $mode): resource|false
  • $command — der auszuführende Shell-Befehl, genau so wie Sie ihn in einem Terminal eingeben würden.
  • $mode — die Pipe-Richtung: "r" zum Lesen der Standardausgabe des Befehls oder "w" zum Schreiben in seine Standardeingabe. Auf manchen Systemen können Sie "b" (binär) oder "t" (Text) hinzufügen, z. B. "rb".

Rückgabewert: eine Dateizeiger-Ressource, die Sie an Funktionen wie fgets() und fwrite() übergeben, oder false, wenn die Pipe nicht geöffnet werden konnte. Der Zeiger ist kein normales Datei-Handle — Sie müssen ihn mit pclose() schließen, niemals mit fclose().

Funktionsweise

Wenn Sie popen() aufrufen, forkt PHP einen Kindprozess, der $command über die Shell ausführt, und verbindet einen seiner Standarddatenströme mit einer Pipe:

  • Im Modus "r" ist die Pipe mit der stdout des Befehls verbunden — Sie lesen, was der Befehl ausgibt.
  • Im Modus "w" ist die Pipe mit der stdin des Befehls verbunden — was Sie schreiben, wird zur Eingabe des Befehls.

Da es per Datenstrom arbeitet, können Sie mit der Verarbeitung der Ausgabe beginnen, bevor der Befehl fertig ist, was den Speicherbedarf auch bei großen Ausgaben gering hält.

Beispiele

Beispiel 1: Ausgabe eines Befehls lesen

<?php

// Read mode: stream the output of a directory listing line by line.
$handle = popen('ls -l', 'r');

if ($handle === false) {
    exit("Could not open the pipe.\n");
}

while (!feof($handle)) {
    $line = fgets($handle);
    echo $line;
}

pclose($handle);

feof() prüft, ob das Ende des Datenstroms erreicht wurde, fgets() liest jeweils eine Zeile, und pclose() schließt die Pipe und wartet, bis der Kindprozess beendet ist. Überprüfen Sie immer den Rückgabewert: Wenn popen() fehlschlägt, gibt es false zurück, und das Lesen von false löst Fehler aus.

Ersetzen Sie unter Windows ls -l durch den entsprechenden Befehl, z. B. dir.

Beispiel 2: In die Eingabe eines Befehls schreiben

<?php

// Write mode: pipe a line of text into grep's standard input.
$handle = popen('grep "example"', 'w');

if ($handle === false) {
    exit("Could not open the pipe.\n");
}

fwrite($handle, "This line has the word example.\n");
fwrite($handle, "This line does not match.\n");

pclose($handle);

Hier sendet fwrite() zwei Zeilen an die Standardeingabe von grep. grep filtert nach dem Wort example, sodass nur die erste Zeile ausgegeben wird. pclose() schließt anschließend die Pipe.

Beispiel 3: Daten direkt komprimieren

<?php

// Stream text straight into gzip and save a compressed file.
$handle = popen('gzip > output.txt.gz', 'w');

if ($handle !== false) {
    fwrite($handle, "Some data to compress.\n");
    pclose($handle);
}

Dies leitet Daten direkt in gzip weiter, ohne eine zwischenzeitliche unkomprimierte Datei zu schreiben.

popen() vs. exec() und shell_exec()

FunktionRückgabewertStreaming?Richtung
popen()eine Pipe-Ressourceja (lesen oder schreiben)einseitig (stdin oder stdout)
exec()letzte Zeile + array der Ausgabeneinnur Ausgabe
shell_exec()vollständige Ausgabe als stringneinnur Ausgabe
proc_open()Prozessressourcejazweiseitig (stdin und stdout)

Verwenden Sie popen(), wenn Sie Streaming benötigen; die anderen, wenn Sie nur das fertige Ergebnis brauchen.

Sicherheit: Niemals rohe Benutzereingaben übergeben

popen() führt sein Argument durch die Shell aus, daher ist jede nicht maskierte Benutzereingabe ein Risiko für Command-Injection. Maskieren Sie Argumente immer, bevor Sie einen Befehl zusammenstellen:

<?php

$userInput = $_GET['name'] ?? 'world';

// escapeshellarg() wraps the value in quotes and neutralizes shell metacharacters.
$command = 'echo Hello ' . escapeshellarg($userInput);

$handle = popen($command, 'r');
echo fgets($handle);
pclose($handle);

Verwenden Sie escapeshellarg() für einzelne Argumente und escapeshellcmd() für ganze Befehle. Am besten vermeiden Sie es ganz, Benutzerdaten an die Shell zu übergeben, wenn eine native PHP-Funktion die Aufgabe erledigen kann.

Häufige Fallstricke

  • pclose() vergessen. Das Offenlassen der Pipe verliert die Ressource und Sie erhalten nie den Exit-Status des Kindprozesses. pclose() gibt den Exit-Code des Befehls zurück.
  • fclose() statt pclose() verwenden. Eine popen()-Ressource ist eine Prozess-Pipe, keine gewöhnliche Datei — schließen Sie sie mit pclose().
  • Rückgabewert false ignorieren. Wenn der Befehl nicht gestartet werden kann, gibt popen() false zurück; prüfen Sie dies vor dem Lesen oder Schreiben.
  • Richtungen mischen. Eine einzelne popen()-Pipe ist entweder nur lesend oder nur schreibend. Für beides verwenden Sie proc_open().

Fazit

popen() öffnet eine einseitige Pipe zu einem Shell-Befehl, damit Sie seine Ausgabe streamen ("r") oder ihm Eingaben zuführen ("w") können, ohne alles im Speicher zu puffern. Überprüfen Sie immer den Rückgabewert, schließen Sie die Pipe mit pclose(), und maskieren Sie Benutzereingaben mit escapeshellarg(), bevor sie die Shell erreichen. Informationen zu den mit der Pipe verwendeten Lese- und Schreibhilfsfunktionen finden Sie unter fgets() und fwrite().

Übung

Übung
Was ist die Funktion des Befehls 'popen' in PHP?
Was ist die Funktion des Befehls 'popen' in PHP?
Was this page helpful?