Problem mit popen / pclose unter Linux



  • Hallo,

    ich hoffe, ich bin im richtigen Bereich mit meiner Frage. Ich habe folgende Methode geschrieben:

    		signed Executable::execute(std::vector<std::string>& result) const
    		{
    			constexpr uint64_t MessageLength = 128;
    			const std::string pathInclParameters(getPath().string() + (parameters.empty() ? StdLibs_Null : (StdLibs_Space + parameters)));
    #ifdef UNIX
    			std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(pathInclParameters.c_str(), "r"), pclose);
    #endif
    
    #ifdef WINDOWS
    			std::unique_ptr<FILE, decltype(&_pclose)> pipe(_popen(pathInclParameters.c_str(), "r"), _pclose);
    #endif
    
    			if (pipe)
    			{
    				try
    				{
    					while (!feof(pipe.get()))
    					{
    						std::array<char, MessageLength> buffer = { 0 };
    
    						if (fgets(buffer.data(), MessageLength, pipe.get()) != nullptr)
    						{
    							std::string localString;
    
    							for (uint64_t index = 0; index < MessageLength; index++)
    							{
    								if ((buffer.at(index) == StdLibs_Character_NewLine) ||
    									(buffer.at(index) == StdLibs_Character_Null))
    								{
    									break;
    								}
    
    								localString += buffer.at(index);
    							}
    
    							result.push_back(localString);
    						}
    					}
    
    #ifdef UNIX
    					return pclose(pipe.get());
    #endif
    
    #ifdef WINDOWS
    					return _pclose(pipe.get());
    #endif
    				}
    				catch (const std::exception& exception)
    				{
    					throw Exception(SomethingGoesWrong, StdLibs_Exception_StackTraceEntry, exception);
    				}
    			}
    
    			throw Exception(PipeCouldNotBeOpened, StdLibs_Exception_StackTraceEntry);
    		}
    

    Ziel dieser Methode ist es, ein beliebiges Programm aufzurufen und deren Ausgabe in einen Vektor zu schreiben, um ihn im späteren Programmverlauf auswerten zu können.

    Unter Windows habe ich keine Probleme, keine Speicherlöcher, alles bestens. Hier baue ich mit Visual Studio 2019 und dem 17er C++ Standard.

    Unter Linux (Ubuntu Desktop, v20) bekomme ich den folgenden Fehler auf der Kommandozeile beim Ausführen:

    free(): double free detected in tcache 2
    Aborted (core dumped)

    Nach einigen Debugversuchen bin ich soweit gekommen, dass der Fehler bei dem Return-Statement auftritt. Ich bin nun leider etwas ratlos. Unter Linux baue ich mit dem G++ (v9.3.0) und ebenso dem 17er C++ Standard.

    Kann jemand helfen?

    lg Torsten



  • Auf den ersten Blick (vom Handy) fällt mir das doppelte pclose auf. Du schließt die Datei im return manuell und durch den unique_ptr zusätzlich automatisch nochmal. Durch return pclose(pointer.release() statt .get()) würdest du nur 1x schließen.

    Edit: ist denn die while-Loop mit dem eof-Test obrn richtig? Das sieht normalerweise immer falsch aus.



  • Danke @wob 🙂

    Ich weiß leider nicht mehr woher ich die Variante habe, würde mich interessieren, ob ich es 1:1übernommen habe oder selbst diesen dummer Fehler eingebaut habe 😞

    Was meinst du mit "falscher while-Schleife"?

    lg Torsten



  • Alle Schleifen, die mit while (!eof(filehandle)) beginnen, sind hochverdächtig. Das ist ein Pattern, das so nicht funktioniert (außer in Pascal).

    Das eof wird erst erkannt, NACHDEM das Ende gelesen wurde. Daher ist eine "normale" Schleife beim Einlesen in der Art while (einlesen erfolgreich) { verarbeite eigelesene Daten }. Wie ich sehe, hast du das mit einem zusätzlichen if um das fgets gelöst - aber dieses "if" kannst du auch direkt im "while" nutzen. Siehe das Beispiel hier: https://en.cppreference.com/w/c/io/fgets

    Wenn du dann aus dem while raus bist, kannst du mit dem eof feststellen, ob du am Ende der Datei bist - und wenn nicht, mit ferror einen Fehler ausgeben.


Anmelden zum Antworten