Wie führe ich einen Befehl aus und erhalte die Ausgabe des Befehls in C++ mit POSIX?
Lesezeit: 14 Minuten
Mischa M
Ich suche nach einer Möglichkeit, die Ausgabe eines Befehls zu erhalten, wenn er in einem C++-Programm ausgeführt wird. Ich habe mir die Verwendung von angeschaut system() Funktion, aber das führt nur einen Befehl aus. Hier ist ein Beispiel für das, was ich suche:
std::string result = system("./some_command");
Ich muss einen beliebigen Befehl ausführen und seine Ausgabe abrufen. Ich habe angeschaut boost.orgaber ich habe nichts gefunden, was mir das geben würde, was ich brauche.
Siehe auch Antworten in dieser Frage:https://stackoverflow.com/questions/52164723/how-to-execute-a-command-and-get-return-code-stdout-and-stderr-of-command-in-c für eine Erweiterung der großartigen Antwort unten, die Methoden bereitstellt, um die zu erhalten return code und stderr ebenso gut wie stdout dass diese Antwort bereits erklärt
– Code_Futter
4. September 2018 um 12:21 Uhr
@code_fodder Sie können einen Link zu stackoverflow.com/questions/52164723/… erstellen.
– Jonas Stein
10. November 2019 um 16:47 Uhr
Hier sind 5 Fragen und Antworten für C und/oder C++, die dieses Thema zu berühren scheinen: 1) Wie liest man von stdout in C, 2) C: Führen Sie einen Systembefehl aus und erhalten Sie eine Ausgabe?, 3) Wie kann ich eine ausführen externes Programm von C und analysieren Sie seine Ausgabe?, 4) stdout von einem system()-Befehl optimal erfassen, 5) (diese Frage).
Ersetzen popen und pclose mit _popen und _pclose für Windows.
Seien Sie sich bewusst, dass dies nur greifen wird stdout und nicht stderr.
– kalaxy
31. Oktober 2011 um 23:53 Uhr
Beachten Sie auch, dass eine Ausnahme auftreten kann in result += bufferso dass das Rohr möglicherweise nicht richtig geschlossen ist.
– Fred Foo
19. Mai 2012 um 20:27 Uhr
Die Antwort ist gut, aber es wäre besser, wenn Sie ‘char* cmd’ durch ‘const char* cmd’ ersetzen.
– fnc12
27. Dezember 2014 um 14:20 Uhr
unique_ptr passt hier besser, da die tatsächliche Referenzanzahl nie verwendet wird.
– Czipperz
29. Mai 2016 um 8:42 Uhr
Ist dies immer noch die beste Methode mit C++17?
– Aaron Franke
27. Februar 2019 um 9:52 Uhr
Jonathan Wakely
Sowohl stdout als auch stderr zu bekommen (und auch in stdin zu schreiben, hier nicht gezeigt) ist mit my ganz einfach pstreams -Header, der iostream-Klassen definiert, die wie funktionieren popen:
#include <pstream.h>
#include <string>
#include <iostream>
int main()
{
// run a process and create a streambuf that reads its stdout and stderr
redi::ipstream proc("./some_command", redi::pstreams::pstdout | redi::pstreams::pstderr);
std::string line;
// read child's stdout
while (std::getline(proc.out(), line))
std::cout << "stdout: " << line << '\n';
# if reading stdout stopped at EOF then reset the state:
if (proc.eof() && proc.fail())
proc.clear();
// read child's stderr
while (std::getline(proc.err(), line))
std::cout << "stderr: " << line << '\n';
}
Ich bin nicht einverstanden. popen erfordert, dass Sie die C stdio-API verwenden, ich bevorzuge die iostreams-API. popen erfordert, dass Sie die manuell bereinigen FILE handhaben, pstreams machen das automatisch. popen akzeptiert nur a const char* Für das Argument, das Sorgfalt erfordert, um Shell-Injection-Angriffe zu vermeiden, ermöglicht Ihnen pstreams, einen Vektor von Zeichenfolgen ähnlich wie zu übergeben execvwas sicherer ist. popen gibt Ihnen nichts als eine Pipe, pstreams teilt Ihnen die PID des Kindes mit, sodass Sie Signale senden können, z. B. um es zu töten, wenn es blockiert ist oder nicht beendet wird. All dies sind Vorteile, selbst wenn Sie nur unidirektionales IO wünschen.
– Jonathan Wakely
10. Oktober 2012 um 17:08 Uhr
Ein weiteres Problem bei dieser Lösung ist, wenn das untergeordnete Element in stderr genug schreibt, um die Puffer zu füllen und zu blockieren, bevor es mit dem Schreiben in stdout beginnt. Das übergeordnete Element blockiert das Lesen von stdout, während das untergeordnete Element darauf wartet, dass stderr gelesen wird. Ressourcen-Deadlock! Mindestens eine dieser Schleifen wäre besser als asynchron (dh Threading).
– Jesse Chisholm
17. Dezember 2015 um 16:54 Uhr
@JesseChisholm, ja, das könnte ein Problem sein. Sie müssen jedoch keine Threads verwenden, da pstreams eine Annäherung an nicht blockierende E/A mithilfe der iostream-Schnittstelle ermöglicht, insbesondere mithilfe von lesenswert Funktion, die die Bereitschaft mit prüft pstreambuf::in_avail(), wird also nicht blockiert. Dies ermöglicht das Demultiplexen auf stdout und stderr des Prozesses, da jeweils Daten verfügbar sind. pstreambuf::in_avail() funktioniert nur 100% zuverlässig, wenn das Betriebssystem das nicht standardmäßige FIONREAD ioctl unterstützt, aber das wird (mindestens) von GNU/Linux und Solaris unterstützt.
– Jonathan Wakely
17. Dezember 2015 um 17:23 Uhr
@chiliNUT die neue Version 1.0.1 verwendet die Boost-Lizenz.
– Jonathan Wakely
3. Februar 2017 um 13:50 Uhr
@JonathanWakely wie kann ich den ipstream nach einer Zeitüberschreitung von 5 Sekunden beenden?
– AK
9. Januar 2020 um 13:04 Uhr
TarmoPikaro
Für Windows, popen funktioniert auch, aber es öffnet ein Konsolenfenster – das schnell über Ihrer UI-Anwendung aufblitzt. Wenn Sie ein Profi sein wollen, ist es besser, dieses “Blinken” zu deaktivieren (insbesondere, wenn der Endbenutzer es abbrechen kann).
Also hier ist meine eigene Version für Windows:
(Dieser Code ist teilweise aus eingeschriebenen Ideen rekombiniert Das Code-Projekt und MSDN-Beispiele.)
#include <windows.h>
#include <atlstr.h>
//
// Execute a command and get the results. (Only standard output)
//
CStringA ExecCmd(
const wchar_t* cmd // [in] command to execute
)
{
CStringA strResult;
HANDLE hPipeRead, hPipeWrite;
SECURITY_ATTRIBUTES saAttr = {sizeof(SECURITY_ATTRIBUTES)};
saAttr.bInheritHandle = TRUE; // Pipe handles are inherited by child process.
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe to get results from child's stdout.
if (!CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0))
return strResult;
STARTUPINFOW si = {sizeof(STARTUPINFOW)};
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdOutput = hPipeWrite;
si.hStdError = hPipeWrite;
si.wShowWindow = SW_HIDE; // Prevents cmd window from flashing.
// Requires STARTF_USESHOWWINDOW in dwFlags.
PROCESS_INFORMATION pi = { 0 };
BOOL fSuccess = CreateProcessW(NULL, (LPWSTR)cmd, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
if (! fSuccess)
{
CloseHandle(hPipeWrite);
CloseHandle(hPipeRead);
return strResult;
}
bool bProcessEnded = false;
for (; !bProcessEnded ;)
{
// Give some timeslice (50 ms), so we won't waste 100% CPU.
bProcessEnded = WaitForSingleObject( pi.hProcess, 50) == WAIT_OBJECT_0;
// Even if process exited - we continue reading, if
// there is some data available over pipe.
for (;;)
{
char buf[1024];
DWORD dwRead = 0;
DWORD dwAvail = 0;
if (!::PeekNamedPipe(hPipeRead, NULL, 0, NULL, &dwAvail, NULL))
break;
if (!dwAvail) // No data available, return
break;
if (!::ReadFile(hPipeRead, buf, min(sizeof(buf) - 1, dwAvail), &dwRead, NULL) || !dwRead)
// Error, the child process might ended
break;
buf[dwRead] = 0;
strResult += buf;
}
} //for
CloseHandle(hPipeWrite);
CloseHandle(hPipeRead);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return strResult;
} //ExecCmd
Dies ist meine Lieblingslösung für Windows, ich hoffe, Sie verzeihen meine Änderungen. Ich würde vorschlagen, den const-cast expliziter zu machen, während ich die explizite Verwendung von in Betracht ziehe wchar_t und CreateProcessW als unnötige Einschränkung.
– Wolf
16. Mai 2017 um 10:13 Uhr
Sehen Sie irgendein Problem oder potenzielles Problem mit dieser Besetzung? Ich ziehe es vor, den Code auf ein Minimum zu beschränken und ihn nicht unnötig zu schreiben.
– TarmoPikaro
17. Mai 2017 um 16:27 Uhr
Nach dem Lesen CreateProcess-Funktion (Windows)sehe ich eine echte Gefahr darin: The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation. Daher ist es vielleicht besser, die Befehlszeile zuerst in einen separaten Puffer zu kopieren, um zu verhindern, dass der Aufrufer seine ursprüngliche Eingabe ändert.
– Wolf
18. Mai 2017 um 12:29 Uhr
Diese Antwort behandelt stderr nicht richtig.
– Refael Sheinker
14. Februar 2019 um 18:39 Uhr
Funktioniert das auch für Unix-Systeme? Oder müsste ich etwas anderes für ein Unix-Gerät verwenden?
– 255.tar.xz
28. Oktober 2019 um 0:28 Uhr
Herr Ree
Ich würde verwenden Popen()(++waqas).
Aber manchmal muss man lesen und schreiben…
Es scheint, als ob niemand mehr die harte Tour macht.
(Angenommen, eine Unix/Linux/Mac-Umgebung oder vielleicht Windows mit einer POSIX-Kompatibilitätsschicht …)
Vielleicht möchten Sie auch mit select() und nicht blockierenden Lesevorgängen herumspielen.
fd_set readfds;
struct timeval timeout;
timeout.tv_sec = 0; /* Seconds */
timeout.tv_usec = 1000; /* Microseconds */
FD_ZERO(&readfds);
FD_SET(childToParent[READ_FD], &readfds);
switch (select (1 + childToParent[READ_FD], &readfds, (fd_set*)NULL, (fd_set*)NULL, & timeout))
{
case 0: /* Timeout expired */
break;
case -1:
if ((errno == EINTR) || (errno == EAGAIN))
{
errno = 0;
break;
}
else
{
FAIL("Select() Failed");
exit(-1);
}
case 1: /* We have input */
readResult = read(childToParent[READ_FD], buffer, BUFFER_SIZE);
// However you want to handle it...
break;
default:
FAIL("How did we see input on more than one file descriptor?");
exit(-1);
}
paxdiablo
Zwei mögliche Vorgehensweisen:
Ich denke nicht popen() ist Teil des C++-Standards (es ist Teil von POSIX aus dem Gedächtnis), aber es ist auf jedem UNIX verfügbar, mit dem ich gearbeitet habe (und Sie scheinen auf UNIX abzuzielen, da Ihr Befehl es ist ./some_command).
Für den unwahrscheinlichen Fall, dass es keine gibt popen()können Sie verwenden system("./some_command >/tmp/some_command.out");verwenden Sie dann die normalen E/A-Funktionen, um die Ausgabedatei zu verarbeiten.
Hansenrik
Das Folgende könnte eine tragbare Lösung sein. Es folgt Standards.
Ich konnte nicht herausfinden, warum popen/pclose fehlt Code::Blöcke/MinGW. Also habe ich das Problem umgangen, indem ich stattdessen CreateProcess() und CreatePipe() verwendet habe.
Hier ist die Lösung, die für mich funktioniert hat:
Danke! Dies ist die beste Popen-Implementierung für Windows im Internet! Und durch das Übergeben des CREATE_NO_WINDOW-Flags kann man endlich die lästigen cmd-Eingabeaufforderungen loswerden, die auftauchen.
– Lacho Tomov
19. Juni 2018 um 18:05 Uhr
Wo passiert man die CREATE_NO_WINDOW Ding?
– Refael Sheinker
14. Februar 2019 um 19:35 Uhr
@Bill Moore, wenn Sie bemerken, enthält Ihre Antwort einen Fehler. ListStdErr wird nie benutzt.
@RefaelSheinker: In der Tat. Ich denke, das zweite ListStdOut += s; sollte durch ersetzt werden ListStdErr += s;, wenn Sie zwei unterschiedliche Zeichenfolgen haben möchten. Ich möchte sie trotzdem zusammenführen, also entferne ich einfach ListStdErr. Schließlich ist Liste ein irgendwie seltsamer Name für eine Zeichenfolge.
– Eric Duminil
7. November 2021 um 12:57 Uhr
10019200cookie-checkWie führe ich einen Befehl aus und erhalte die Ausgabe des Befehls in C++ mit POSIX?yes
Siehe auch Antworten in dieser Frage:
https://stackoverflow.com/questions/52164723/how-to-execute-a-command-and-get-return-code-stdout-and-stderr-of-command-in-c
für eine Erweiterung der großartigen Antwort unten, die Methoden bereitstellt, um die zu erhaltenreturn code
undstderr
ebenso gut wiestdout
dass diese Antwort bereits erklärt– Code_Futter
4. September 2018 um 12:21 Uhr
@code_fodder Sie können einen Link zu stackoverflow.com/questions/52164723/… erstellen.
– Jonas Stein
10. November 2019 um 16:47 Uhr
Hier sind 5 Fragen und Antworten für C und/oder C++, die dieses Thema zu berühren scheinen: 1) Wie liest man von stdout in C, 2) C: Führen Sie einen Systembefehl aus und erhalten Sie eine Ausgabe?, 3) Wie kann ich eine ausführen externes Programm von C und analysieren Sie seine Ausgabe?, 4) stdout von einem system()-Befehl optimal erfassen, 5) (diese Frage).
– Gabriel Staples
1. Februar 2021 um 22:11 Uhr