Ein Dateistream ist definiert als :
genau dann vollständig gepuffert, wenn festgestellt werden kann, dass kein interaktives Gerät verwendet wird
Da Sie in die Standardeingabe umleiten, ist stdin nicht interaktiv und daher gepuffert.
getchar
ist eine Stream-Funktion und bewirkt, dass der Puffer aus dem Stream gefüllt wird, diese Bytes verbraucht und dann ein einzelnes Byte an Sie zurückgibt. system
führt nur fork-exec aus , sodass der Unterprozess alle geöffneten Dateideskriptoren unverändert erbt. Beim bash
Versuch, von der Standardeingabe zu lesen, wird festgestellt, dass sie sich bereits am Ende der Datei befindet, da der gesamte Inhalt bereits von Ihrem übergeordneten Prozess gelesen wurde.
In Ihrem Fall möchten Sie nur dieses einzelne Byte verbrauchen, bevor Sie es dem untergeordneten Prozess übergeben.
Die setvbuf()
Funktion kann verwendet werden, nachdem der Stream, auf den der Stream zeigt, einer geöffneten Datei zugeordnet ist, bevor jedoch eine andere [...] Operation für den Stream ausgeführt wird.
So fügen Sie einen geeigneten Anruf vor dem getchar()
:
#include <stdio.h>
#include <stdlib.h>
int main() {
setvbuf(stdin, NULL, _IONBF, 0 );
getchar();
system("/bin/bash -i");
return 0;
}
wird tun, was Sie wollen, indem Sie stdin
auf ungepuffert setzen ( _IONBF
). getchar
bewirkt, dass nur ein einzelnes Byte gelesen wird und der Rest der Eingabe für den Unterprozess verfügbar ist. read
In diesem Fall ist es möglicherweise besser, stattdessen die gesamte Streams-Schnittstelle zu verwenden.
POSIX schreibt bestimmte Verhaltensweisen vor, wenn nach einem Fork von beiden Prozessen aus auf das Handle zugegriffen werden kann, merkt dies jedoch ausdrücklich an
Wenn die einzige Aktion, die von einem der Prozesse ausgeführt wird, eine der exec
Funktionen [...] ist, wird in diesem Prozess niemals auf das Handle zugegriffen.
was bedeutet, dass system()
das nichts Besonderes damit zu tun hat, da es nur Fork-Exec ist .
Dies ist wahrscheinlich das, was Ihre fork
Problemumgehung trifft. Wenn der Griff auf beiden Seiten zugänglich ist, dann für den ersten :
Wenn der Stream mit einem Modus geöffnet ist, der das Lesen ermöglicht, und sich die zugrunde liegende Beschreibung der geöffneten Datei auf ein Gerät bezieht, das suchen kann, muss die Anwendung entweder einen ausführen fflush()
oder der Stream wird geschlossen.
Das Aufrufen fflush()
eines Lesestreams bedeutet Folgendes:
Der Dateiversatz der zugrunde liegenden Beschreibung der geöffneten Datei wird auf die Dateiposition des Streams gesetzt
Daher sollte die Deskriptorposition auf 1 Byte zurückgesetzt werden, genau wie die des Streams, und der nachfolgende Unterprozess erhält ab diesem Punkt seine Standardeingabe.
Zusätzlich für den zweiten (Kinder-) Griff :
Wenn ein zuvor aktives Handle von einer Funktion verwendet wurde, die den Dateiversatz explizit geändert hat, außer wie oben für das erste Handle erforderlich, muss die Anwendung ein lseek()
oder fseek()
(je nach Handle-Typ) an einer geeigneten Stelle ausführen .
und ich nehme an, "ein geeigneter Ort" könnte der gleiche sein (obwohl es nicht weiter spezifiziert ist). Der getchar()
Aufruf "hat den Dateiversatz explizit geändert", daher sollte dieser Fall zutreffen. Die Absicht der Passage ist, dass das Arbeiten in beiden Zweigen der Gabel den gleichen Effekt haben sollte, also beide fork() > 0
und fork() == 0
das gleiche funktionieren sollten. Da in diesem Zweig jedoch tatsächlich nichts passiert, ist es fraglich, ob keine dieser Regeln für Eltern oder Kinder verwendet werden sollte.
Das genaue Ergebnis ist wahrscheinlich plattformabhängig - zumindest wird nicht direkt angegeben, was als "jemals zugänglich" gilt, und welches Handle das erste und zweite ist. Es gibt auch einen früheren, übergeordneten Fall für den übergeordneten Prozess:
Wenn die einzige weitere Aktion, die an einem Handle für diesen geöffneten Dateideskriptor ausgeführt werden muss, darin besteht, ihn zu schließen, muss keine Aktion ausgeführt werden.
Dies gilt wohl für Ihr Programm, da es erst danach beendet wird. Wenn dies der Fall ist fflush()
, sollten alle verbleibenden Fälle, einschließlich der , übersprungen werden, und das angezeigte Verhalten wäre eine Abweichung von der Spezifikation. Es ist fraglich, ob das Aufrufen fork()
eine Aktion für das Handle darstellt, aber nicht explizit oder offensichtlich, also würde ich dem nicht vertrauen. Es gibt auch genug "entweder" und "oder" in den Anforderungen, so dass viele Variationen zulässig erscheinen.
Aus mehreren Gründen denke ich, dass das Verhalten, das Sie sehen, ein Fehler oder zumindest eine großzügige Interpretation der Spezifikation sein kann. Meine allgemeine Lesart ist, dass, da in jedem Fall ein Zweig der fork
nichts tut, keine dieser Regeln angewendet werden sollte und die Deskriptorposition ignoriert werden sollte, wo sie war. Ich kann das nicht definitiv sagen, aber es scheint die einfachste Lektüre zu sein.
Ich würde mich nicht darauf verlassen, dass die fork
Technik funktioniert. Ihre dritte Version funktioniert hier bei mir nicht. Verwenden Siesetbuf
setvbuf
stattdessen / . Wenn möglich, würde ich sogar popen
oder ähnlich verwenden, um den Prozess mit der erforderlichen Filterung explizit einzurichten, anstatt mich auf die Unklarheiten der Stream- und Dateideskriptor-Interaktionen zu verlassen.
-c
ohne Befehl ausgegeben wird ?