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

463

Ich suche nach einer Möglichkeit, die Ausgabe eines Befehls abzurufen, wenn dieser in einem C ++ - Programm ausgeführt wird. Ich habe system()versucht, die Funktion zu verwenden, aber das wird nur einen Befehl ausführen. Hier ist ein Beispiel für das, wonach 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 ich habe nichts gefunden, was mir das gibt, was ich brauche.

Mischa M.
quelle
Siehe auch die Antworten in dieser Frage: /programming/52164723/how-to-execute-a-command-and-get-return-code-stdout-and-stderr-of-command-in-cfür eine Erweiterung der großartigen Antwort unten, die Methoden zum return codestderrstdout
Abrufen
2
@code_fodder Sie können einen Link zu stackoverflow.com/questions/52164723/…
Jonas Stein

Antworten:

599
#include <cstdio>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <array>

std::string exec(const char* cmd) {
    std::array<char, 128> buffer;
    std::string result;
    std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
    if (!pipe) {
        throw std::runtime_error("popen() failed!");
    }
    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
        result += buffer.data();
    }
    return result;
}

Pre-C ++ 11 Version:

#include <iostream>
#include <stdexcept>
#include <stdio.h>
#include <string>

std::string exec(const char* cmd) {
    char buffer[128];
    std::string result = "";
    FILE* pipe = popen(cmd, "r");
    if (!pipe) throw std::runtime_error("popen() failed!");
    try {
        while (fgets(buffer, sizeof buffer, pipe) != NULL) {
            result += buffer;
        }
    } catch (...) {
        pclose(pipe);
        throw;
    }
    pclose(pipe);
    return result;
}

Ersetzen popenund pclosemit _popenund _pclosefür Windows.

waqas
quelle
69
Beachten Sie, dass dies nur stdout und nicht stderr erfasst .
Kalaxy
14
Beachten Sie auch, dass in eine Ausnahme auftreten kann result += buffer, sodass das Rohr möglicherweise nicht richtig geschlossen wird.
Fred Foo
6
@Yasky: Wenn das Programm ausgeführt wird int main(){ puts("ERROR"); }.
Dreamlax
8
Die Antwort ist gut, aber es wäre besser, wenn Sie 'char * cmd' durch 'const char * cmd'
ersetzen
29
unique_ptr passt hier besser, wo der tatsächliche Referenzzähler nie verwendet wird.
Czipperz
77

Fehler- und Standardausgaben immer (und auch stdin schreiben, hier nicht dargestellt) ist leicht peasy mit meinem pstreams Header, der definierten Klassen iostream , dass die Arbeit wie 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';
} 
Jonathan Wakely
quelle
18
Ich stimme dir nicht zu. popenWenn Sie die C stdio-API verwenden müssen, bevorzuge ich die iostreams-API. popenerfordert, dass Sie den FILEGriff manuell bereinigen , pstreams tun dies automatisch. Akzeptiert popennur ein const char*für das Argument, das Sorgfalt erfordert, um Shell-Injection-Angriffe zu vermeiden. Mit pstreams können Sie einen Vektor mit ähnlichen Zeichenfolgen übergeben execv, was sicherer ist. popengibt dir nichts als eine Pipe, pstreams sagt dir die PID des Kindes, so dass du Signale senden kannst, z. B. um es zu töten, wenn es blockiert ist oder nicht beendet wird. All dies sind Vorteile, auch wenn Sie nur unidirektionale E / A möchten.
Jonathan Wakely
1
Ein weiteres Problem bei dieser Lösung besteht darin, dass das Kind in stderr genug schreibt, um die Puffer zu füllen und zu blockieren, bevor es mit dem Schreiben in stdout beginnt. Der Elternteil blockiert das Lesen von stdout, während das Kind blockiert ist und darauf wartet, dass stderr gelesen wird. Ressourcen-Deadlock! Mindestens eine dieser Schleifen wäre besser als asynchron (dh mit Thread).
Jesse Chisholm
1
@ 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 der Readsome- Funktion, die die Bereitschaft mithilfe überprüft pstreambuf::in_avail()und daher nicht blockiert. Dies ermöglicht das Demultiplexen von stdout und stderr des Prozesses, da jeweils Daten verfügbar sind. pstreambuf::in_avail()funktioniert nur zu 100% zuverlässig, wenn das Betriebssystem das nicht standardmäßige FIONREAD ioctl unterstützt, dies wird jedoch (zumindest) unter GNU / Linux und Solaris unterstützt.
Jonathan Wakely
13
@chiliNUT Die neue Version 1.0.1 verwendet die Boost-Lizenz.
Jonathan Wakely
1
@ JonathanWakely, wie kann ich den IPstream nach einer Zeitüberschreitung von 5 Sekunden beenden?
AK
34

Ich würde popen () (++ waqas) verwenden .

Aber manchmal muss man lesen und schreiben ...

Es scheint, als würde niemand mehr auf die harte Tour gehen.

(Angenommen, eine Unix / Linux / Mac-Umgebung oder Windows mit einer POSIX-Kompatibilitätsschicht ...)

enum PIPE_FILE_DESCRIPTERS
{
  READ_FD  = 0,
  WRITE_FD = 1
};

enum CONSTANTS
{
  BUFFER_SIZE = 100
};

int
main()
{
  int       parentToChild[2];
  int       childToParent[2];
  pid_t     pid;
  string    dataReadFromChild;
  char      buffer[BUFFER_SIZE + 1];
  ssize_t   readResult;
  int       status;

  ASSERT_IS(0, pipe(parentToChild));
  ASSERT_IS(0, pipe(childToParent));

  switch (pid = fork())
  {
    case -1:
      FAIL("Fork failed");
      exit(-1);

    case 0: /* Child */
      ASSERT_NOT(-1, dup2(parentToChild[READ_FD], STDIN_FILENO));
      ASSERT_NOT(-1, dup2(childToParent[WRITE_FD], STDOUT_FILENO));
      ASSERT_NOT(-1, dup2(childToParent[WRITE_FD], STDERR_FILENO));
      ASSERT_IS(0, close(parentToChild [WRITE_FD]));
      ASSERT_IS(0, close(childToParent [READ_FD]));

      /*     file, arg0, arg1,  arg2 */
      execlp("ls", "ls", "-al", "--color");

      FAIL("This line should never be reached!!!");
      exit(-1);

    default: /* Parent */
      cout << "Child " << pid << " process running..." << endl;

      ASSERT_IS(0, close(parentToChild [READ_FD]));
      ASSERT_IS(0, close(childToParent [WRITE_FD]));

      while (true)
      {
        switch (readResult = read(childToParent[READ_FD],
                                  buffer, BUFFER_SIZE))
        {
          case 0: /* End-of-File, or non-blocking read. */
            cout << "End of file reached..."         << endl
                 << "Data received was ("
                 << dataReadFromChild.size() << "): " << endl
                 << dataReadFromChild                << endl;

            ASSERT_IS(pid, waitpid(pid, & status, 0));

            cout << endl
                 << "Child exit staus is:  " << WEXITSTATUS(status) << endl
                 << endl;

            exit(0);


          case -1:
            if ((errno == EINTR) || (errno == EAGAIN))
            {
              errno = 0;
              break;
            }
            else
            {
              FAIL("read() failed");
              exit(-1);
            }

          default:
            dataReadFromChild . append(buffer, readResult);
            break;
        }
      } /* while (true) */
  } /* switch (pid = fork())*/
}

Möglicherweise 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);
}
Mr.Ree
quelle
1
Der harte Weg ist richtig :) Ich mag die Idee mit select () -Aufruf, obwohl ich in diesem Fall tatsächlich warten muss, bis die Aufgabe abgeschlossen ist. Ich werde diesen Code für ein anderes Projekt behalten, das ich habe :)
Misha M
4
... oder Sie könnten die vorhandene Funktion posix_spawnp verwenden
Per Johansson
5
Ihr execlpAufruf hat einen Fehler: Der letzte übergebene argZeiger muss sein, (char *) NULLum die Liste der variadischen Argumente ordnungsgemäß zu beenden (siehe execlp(3)Referenz).
Kristóf Marussy
Funktioniert dies unter Unix, Linux und Windows? Können Sie bitte auch Header-Dateien?
Kittu
Wohin übergeben Sie die .bat-Datei im Code?
Kittu
33

Funktioniert auch unter Windows, popenöffnet jedoch ein Konsolenfenster, das schnell über Ihre UI-Anwendung blinkt. Wenn Sie ein Profi sein möchten, 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 Ideen in The Code Project- und MSDN-Beispielen neu kombiniert .)

#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
TarmoPikaro
quelle
1
Dies ist meine Lieblingslösung für Windows. Ich hoffe, Sie verzeihen meine Änderungen. Ich würde vorschlagen, die const-Besetzung expliziter zu gestalten, während ich die explizite Verwendung von wchar_tund CreateProcessWals unnötige Einschränkung betrachte.
Wolf
Sehen Sie ein Problem oder ein potenzielles Problem mit dieser Besetzung? Ich ziehe es vor, den Code auf ein Minimum zu beschränken und ihn nicht ohne Notwendigkeit zu schreiben.
TarmoPikaro
4
Nach dem Lesen der CreateProcess-Funktion (Windows) sehe ich dabei eine echte Gefahr: 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 möglicherweise besser, die Befehlszeile zuerst in einen separaten Puffer zu kopieren, um zu verhindern, dass der Aufrufer seine ursprüngliche Eingabe ändert.
Wolf
Diese Antwort behandelt stderr nicht richtig.
Refael Sheinker
Funktioniert das auch für Unix-Systeme? Oder müsste ich etwas anderes für ein Unix-Gerät verwenden?
255.tar.xz
17

Zwei mögliche Ansätze:

  1. Ich denke nicht, dass popen()es Teil des C ++ - Standards ist (es ist Teil von POSIX aus dem Speicher), aber es ist auf jedem UNIX verfügbar, mit dem ich gearbeitet habe (und Sie scheinen auf UNIX abzuzielen, da Ihr Befehl lautet ./some_command).

  2. Wenn dies nicht der Fall ist popen(), können Sie system("./some_command >/tmp/some_command.out");die Ausgabedatei mit den normalen E / A-Funktionen verarbeiten.

paxdiablo
quelle
Vielen Dank für Popen, ich werde das jetzt verwenden und mich um Nicht-POSIX-Systeme kümmern, wenn das auftaucht.
Mischa M
8

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

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

using namespace std;

int SystemCapture(
    string         CmdLine,    //Command Line
    string         CmdRunDir,  //set to '.' for current directory
    string&        ListStdOut, //Return List of StdOut
    string&        ListStdErr, //Return List of StdErr
    uint32_t&      RetCode)    //Return Exit Code
{
    int                  Success;
    SECURITY_ATTRIBUTES  security_attributes;
    HANDLE               stdout_rd = INVALID_HANDLE_VALUE;
    HANDLE               stdout_wr = INVALID_HANDLE_VALUE;
    HANDLE               stderr_rd = INVALID_HANDLE_VALUE;
    HANDLE               stderr_wr = INVALID_HANDLE_VALUE;
    PROCESS_INFORMATION  process_info;
    STARTUPINFO          startup_info;
    thread               stdout_thread;
    thread               stderr_thread;

    security_attributes.nLength              = sizeof(SECURITY_ATTRIBUTES);
    security_attributes.bInheritHandle       = TRUE;
    security_attributes.lpSecurityDescriptor = nullptr;

    if (!CreatePipe(&stdout_rd, &stdout_wr, &security_attributes, 0) ||
            !SetHandleInformation(stdout_rd, HANDLE_FLAG_INHERIT, 0)) {
        return -1;
    }

    if (!CreatePipe(&stderr_rd, &stderr_wr, &security_attributes, 0) ||
            !SetHandleInformation(stderr_rd, HANDLE_FLAG_INHERIT, 0)) {
        if (stdout_rd != INVALID_HANDLE_VALUE) CloseHandle(stdout_rd);
        if (stdout_wr != INVALID_HANDLE_VALUE) CloseHandle(stdout_wr);
        return -2;
    }

    ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));
    ZeroMemory(&startup_info, sizeof(STARTUPINFO));

    startup_info.cb         = sizeof(STARTUPINFO);
    startup_info.hStdInput  = 0;
    startup_info.hStdOutput = stdout_wr;
    startup_info.hStdError  = stderr_wr;

    if(stdout_rd || stderr_rd)
        startup_info.dwFlags |= STARTF_USESTDHANDLES;

    // Make a copy because CreateProcess needs to modify string buffer
    char      CmdLineStr[MAX_PATH];
    strncpy(CmdLineStr, CmdLine.c_str(), MAX_PATH);
    CmdLineStr[MAX_PATH-1] = 0;

    Success = CreateProcess(
        nullptr,
        CmdLineStr,
        nullptr,
        nullptr,
        TRUE,
        0,
        nullptr,
        CmdRunDir.c_str(),
        &startup_info,
        &process_info
    );
    CloseHandle(stdout_wr);
    CloseHandle(stderr_wr);

    if(!Success) {
        CloseHandle(process_info.hProcess);
        CloseHandle(process_info.hThread);
        CloseHandle(stdout_rd);
        CloseHandle(stderr_rd);
        return -4;
    }
    else {
        CloseHandle(process_info.hThread);
    }

    if(stdout_rd) {
        stdout_thread=thread([&]() {
            DWORD  n;
            const size_t bufsize = 1000;
            char         buffer [bufsize];
            for(;;) {
                n = 0;
                int Success = ReadFile(
                    stdout_rd,
                    buffer,
                    (DWORD)bufsize,
                    &n,
                    nullptr
                );
                printf("STDERR: Success:%d n:%d\n", Success, (int)n);
                if(!Success || n == 0)
                    break;
                string s(buffer, n);
                printf("STDOUT:(%s)\n", s.c_str());
                ListStdOut += s;
            }
            printf("STDOUT:BREAK!\n");
        });
    }

    if(stderr_rd) {
        stderr_thread=thread([&]() {
            DWORD        n;
            const size_t bufsize = 1000;
            char         buffer [bufsize];
            for(;;) {
                n = 0;
                int Success = ReadFile(
                    stderr_rd,
                    buffer,
                    (DWORD)bufsize,
                    &n,
                    nullptr
                );
                printf("STDERR: Success:%d n:%d\n", Success, (int)n);
                if(!Success || n == 0)
                    break;
                string s(buffer, n);
                printf("STDERR:(%s)\n", s.c_str());
                ListStdOut += s;
            }
            printf("STDERR:BREAK!\n");
        });
    }

    WaitForSingleObject(process_info.hProcess,    INFINITE);
    if(!GetExitCodeProcess(process_info.hProcess, (DWORD*) &RetCode))
        RetCode = -1;

    CloseHandle(process_info.hProcess);

    if(stdout_thread.joinable())
        stdout_thread.join();

    if(stderr_thread.joinable())
        stderr_thread.join();

    CloseHandle(stdout_rd);
    CloseHandle(stderr_rd);

    return 0;
}

int main()
{
    int            rc;
    uint32_t       RetCode;
    string         ListStdOut;
    string         ListStdErr;

    cout << "STARTING.\n";

    rc = SystemCapture(
        "C:\\Windows\\System32\\ipconfig.exe",    //Command Line
        ".",                                     //CmdRunDir
        ListStdOut,                              //Return List of StdOut
        ListStdErr,                              //Return List of StdErr
        RetCode                                  //Return Exit Code
    );
    if (rc < 0) {
        cout << "ERROR: SystemCapture\n";
    }

    cout << "STDOUT:\n";
    cout << ListStdOut;

    cout << "STDERR:\n";
    cout << ListStdErr;

    cout << "Finished.\n";

    cout << "Press Enter to Continue";
    cin.ignore();

    return 0;
}
Bill Moore
quelle
5
Vielen Dank! Dies ist die beste Popen-Implementierung für Windows im Internet! Und wenn Sie das CREATE_NO_WINDOW-Flag übergeben, können Sie endlich die lästigen cmd-Eingabeaufforderungen entfernen, die angezeigt werden.
Lacho Tomov
1
Wo kommst du an dem CREATE_NO_WINDOWDing vorbei?
Refael Sheinker
2
@ Bill Moore, wenn Sie bemerken, gibt es einen Fehler in Ihrer Antwort. ListStdErrwird nie benutzt.
Refael Sheinker
7

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;
}

// For 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;
}
hanshenrik
quelle
4
Ich bekomme diese Warnung mit gcc: "Warnung: Die Verwendung von tmpnamist gefährlich, bessere Verwendung mkstemp"
Mark Lakata
4

Angenommen, POSIX, einfacher Code zum Erfassen von stdout:

#include <sys/wait.h>
#include <unistd.h>
#include <string>
#include <vector>

std::string qx(const std::vector<std::string>& 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<char*> vc(args.size() + 1, 0);
    for (size_t i = 0; i < args.size(); ++i) {
      vc[i] = const_cast<char*>(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 für weitere Funktionen willkommen:

https://github.com/ericcurtin/execxx

Ericcurtin
quelle
2

Sie können die Ausgabe erhalten, nachdem Sie ein Skript mit einer Pipe ausgeführt haben. Wir verwenden Pipes, wenn wir die Ausgabe des untergeordneten Prozesses wünschen.

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 Sie ausführen möchten. Fügen Sie es in eine Befehlsvariable mit den Argumenten ein, die Ihr Skript akzeptiert (nichts, wenn keine Argumente). Und die Datei, in der Sie die Ausgabe des Skripts erfassen möchten, legen Sie sie in copy_fp ab.

Der Popen führt also Ihr Skript aus und legt die Ausgabe in fpipe ab. Anschließend können Sie einfach alles davon in Ihre Ausgabedatei kopieren.

Auf diese Weise können Sie die Ausgaben untergeordneter Prozesse erfassen.

Und ein anderer Prozess ist, dass Sie das direkt setzen können > Operator nur in den Befehl einfügen können. Wenn wir also alles in eine Datei einfügen, während wir den Befehl ausführen, müssen Sie nichts kopieren.

In diesem Fall müssen keine Rohre verwendet werden. Sie können just verwenden system, und es wird der Befehl ausgeführt und die Ausgabe in diese Datei gestellt.

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;
}

Weitere Informationen finden Sie im YoLinux-Lernprogramm: Fork- , Exec- und Prozesssteuerung .

Muskan Agarwal
quelle
1

Beachten Sie, dass Sie eine Ausgabe erhalten können, indem Sie die Ausgabe in die Datei umleiten und dann lesen

Es wurde in der Dokumentation von gezeigt std::system

Sie können den Exit-Code erhalten, indem Sie das WEXITSTATUSMakro aufrufen .

    int status = std::system("ls -l >test.txt"); // execute the UNIX command "ls -l >test.txt"
    std::cout << std::ifstream("test.txt").rdbuf();
    std::cout << "Exit code: " << WEXITSTATUS(status) << std::endl;
Pikacz
quelle