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ßefind-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 ingzip,mailoder 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 -ldurch 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()
| Funktion | Rückgabewert | Streaming? | Richtung |
|---|---|---|---|
popen() | eine Pipe-Ressource | ja (lesen oder schreiben) | einseitig (stdin oder stdout) |
exec() | letzte Zeile + array der Ausgabe | nein | nur Ausgabe |
shell_exec() | vollständige Ausgabe als string | nein | nur Ausgabe |
proc_open() | Prozessressource | ja | zweiseitig (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()stattpclose()verwenden. Einepopen()-Ressource ist eine Prozess-Pipe, keine gewöhnliche Datei — schließen Sie sie mitpclose().- Rückgabewert
falseignorieren. Wenn der Befehl nicht gestartet werden kann, gibtpopen()falsezurü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 Sieproc_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().