607 Stimmen

Wie führe ich einen Befehl aus und erhalte die Ausgabe des Befehls innerhalb von C++ mit POSIX?

Ich suche nach einem Weg, um die Ausgabe eines Befehls zu erhalten, wenn er aus einem C++-Programm heraus ausgeführt wird. Ich habe versucht, die system()-Funktion zu verwenden, aber das führt nur einen Befehl aus. Hier ist ein Beispiel dafür, was ich suche:

std::string result = system("./some_command");

Ich muss einen beliebigen Befehl ausführen und seine Ausgabe erhalten. Ich habe mir boost.org angesehen, aber nichts gefunden, das mir das geben würde, was ich brauche.

3 Stimmen

Auch siehe Antworten in dieser Frage: https://stackoverflow.com/questions/52164723/how-to‌​-execute-a-command-a‌​nd-get-return-code-s‌​tdout-and-stderr-of-‌​command-in-c für eine Erweiterung der großartigen Antwort unten, die Methoden zum Abrufen des Rückgabecodes und von stderr sowie stdout bereitstellt, die diese Antwort bereits erklärt.

8 Stimmen

@code_fodder Sie können einen Link zu stackoverflow.com/questions/52164723/… erstellen.

12voto

Bimo Punkte 5077

Ich konnte nicht herausfinden, warum popen/pclose in Code::Blocks/MinGW fehlen. 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:

//C++11
#include <cstdio>
#include <iostream>
#include <windows.h>
#include <cstdint>
#include <deque>
#include <string>
#include <thread>

using namespace std;

int SystemCapture(
    string         CmdLine,    //Befehlszeile
    string         CmdRunDir,  //auf '.' für das aktuelle Verzeichnis setzen
    string&        ListStdOut, //Liste von StdOut zurückgeben
    string&        ListStdErr, //Liste von StdErr zurückgeben
    uint32_t&      RetCode)    //Exit-Code zurückgeben
{
    // Der Rest des Codes bleibt unverändert...
}

int main()
{
    // Der Rest des Codes bleibt unverändert...
}

6 Stimmen

Vielen Dank! Dies ist die beste popen-Implementierung für Windows im Internet! Und durch das Weitergeben des CREATE_NO_WINDOW-Flags kann man endlich die lästigen cmd-Prompts loswerden, die auftauchen.

1 Stimmen

Wo gibst du das CREATE_NO_WINDOW Ding hin?

3 Stimmen

@Bill Moore, wenn Sie bemerken, gibt es einen Fehler in Ihrer Antwort. ListStdErr wird nie verwendet.

11voto

Das Folgende könnte eine tragbare Lösung sein. Es folgt Standards.

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include <sstream>

std::string ssystem (const char *command) {
    char tmpname [L_tmpnam];
    std::tmpnam ( tmpname );
    std::string scommand = command;
    std::string cmd = scommand + " >> " + tmpname;
    std::system(cmd.c_str());
    std::ifstream file(tmpname, std::ios::in | std::ios::binary );
    std::string result;
    if (file) {
        while (!file.eof()) result.push_back(file.get())
            ;
        file.close();
    }
    remove(tmpname);
    return result;
}

// Für Cygwin

int main(int argc, char *argv[])
{
    std::string bash = "FILETWO=/cygdrive/c/*\nfor f in $FILETWO\ndo\necho \"$f\"\ndone ";
    std::string in;
    std::string s = ssystem(bash.c_str());
    std::istringstream iss(s);
    std::string line;
    while (std::getline(iss, line))
    {
        std::cout << "LINE-> " + line + "  length: " << line.length() << std::endl;
    }
    std::cin >> in;
    return 0;
}

4 Stimmen

Ich bekomme diese Warnung mit gcc: "Warnung: Die Verwendung von tmpnam ist gefährlich, besser mkstemp verwenden"

11voto

Pikacz Punkte 392

Beachten Sie, dass Sie den Output erhalten können, indem Sie den Output in die Datei umleiten und diese dann lesen

Es wurde in der Dokumentation von std::system gezeigt

Sie können den Exit-Code aufrufen, indem Sie das Makro WEXITSTATUS aufrufen.

    int status = std::system("ls -l >test.txt"); // UNIX-Befehl "ls -l >test.txt" ausführen
    std::cout << std::ifstream("test.txt").rdbuf();
    std::cout << "Exit-Code: " << WEXITSTATUS(status) << std::endl;

0 Stimmen

Dies funktioniert nur teilweise. Wenn Sie die ID des gestarteten Prozesses abrufen möchten, funktioniert es nicht.

4voto

ericcurtin Punkte 1350

Assuming POSIX, einfacher Code zum Erfassen von stdout:

#include 
#include 
#include 
#include 

std::string qx(const std::vector& args) {
  int stdout_fds[2];
  pipe(stdout_fds);

  int stderr_fds[2];
  pipe(stderr_fds);

  const pid_t pid = fork();
  if (!pid) {
    close(stdout_fds[0]);
    dup2(stdout_fds[1], 1);
    close(stdout_fds[1]);

    close(stderr_fds[0]);
    dup2(stderr_fds[1], 2);
    close(stderr_fds[1]);

    std::vector vc(args.size() + 1, 0);
    for (size_t i = 0; i < args.size(); ++i) {
      vc[i] = const_cast(args[i].c_str());
    }

    execvp(vc[0], &vc[0]);
    exit(0);
  }

  close(stdout_fds[1]);

  std::string out;
  const int buf_size = 4096;
  char buffer[buf_size];
  do {
    const ssize_t r = read(stdout_fds[0], buffer, buf_size);
    if (r > 0) {
      out.append(buffer, r);
    }
  } while (errno == EAGAIN || errno == EINTR);

  close(stdout_fds[0]);

  close(stderr_fds[1]);
  close(stderr_fds[0]);

  int r, status;
  do {
    r = waitpid(pid, &status, 0);
  } while (r == -1 && errno == EINTR);

  return out;
}

Code-Beiträge sind willkommen für mehr Funktionalität:

https://github.com/ericcurtin/execxx

4voto

Muskan Agarwal Punkte 328

Du kannst die Ausgabe nach Ausführung eines Skripts mittels eines Pipes erhalten. Pipes werden verwendet, wenn wir die Ausgabe des Kindprozesses möchten.

int my_func() {
    char ch;
    FILE *fpipe;
    FILE *copy_fp;
    FILE *tmp;
    char *command = (char *)"/usr/bin/my_script my_arg";
    copy_fp = fopen("/tmp/output_file_path", "w");
    fpipe = (FILE *)popen(command, "r");
    if (fpipe) {
        while ((ch = fgetc(fpipe)) != EOF) {
            fputc(ch, copy_fp);
        }
    }
    else {
        if (copy_fp) {
            fprintf(copy_fp, "Sorry there was an error opening the file");
        }
    }
    pclose(fpipe);
    fclose(copy_fp);
    return 0;
}

Hier ist also das Skript, das du ausführen möchtest. Setze es in einer Befehlsvariable mit den Argumenten, die dein Skript benötigt (nichts, wenn keine Argumente). Und die Datei, in der du die Ausgabe des Skripts erfassen möchtest, packe es in copy_fp.

Also führt das popen dein Skript aus und legt die Ausgabe in fpipe ab und dann kannst du einfach alles davon in deine Ausgabedatei kopieren.

Auf diese Weise kannst du die Ausgaben von Kindprozessen erfassen.

Und ein weiterer Prozess ist, dass du den > Operator direkt im Befehl platzieren kannst. Also wenn wir alles in eine Datei schreiben, während wir den Befehl ausführen, musst du nichts kopieren.

In diesem Fall gibt es keinen Bedarf, Pipes zu verwenden. Du kannst einfach system verwenden und es wird den Befehl ausführen und die Ausgabe in diese Datei legen.

int my_func(){
    char *command = (char *)"/usr/bin/my_script my_arg > /tmp/my_putput_file";
    system(command);
    printf("everything saved in my_output_file");
    return 0;
}

Du kannst den YoLinux Tutorial: Fork, Exec und Prozesskontrolle für weitere Informationen lesen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X