Jak wykonać polecenie i uzyskać wynik polecenia w C ++ przy użyciu POSIX?

463

Szukam sposobu na uzyskanie danych wyjściowych polecenia, które jest uruchamiane z poziomu programu C ++. Patrzyłem na użycie system()funkcji, ale to po prostu wykona polecenie. Oto przykład tego, czego szukam:

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

Muszę uruchomić dowolne polecenie i uzyskać jego wynik. Spojrzałem na boost.org , ale nie znalazłem niczego, co dałoby mi to, czego potrzebuję.

Misha M.
źródło
Zobacz także odpowiedzi w tym pytaniu: /programming/52164723/how-to-execute-a-command-and-get-return-code-stdout-and-stderr-of-command-in-cdla rozszerzenia świetnej odpowiedzi poniżej, która zawiera metody uzyskiwania, return codea stderrtakże, stdoutże ta odpowiedź już wyjaśnia
code_fodder
2
@code_fodder możesz utworzyć link do stackoverflow.com/questions/52164723/...
Jonas Stein

Odpowiedzi:

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

Wersja wcześniejsza niż C ++ 11:

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

Wymień popeni pcloseze _popeni _pclosedla Windows.

waqas
źródło
69
Pamiętaj, że będzie to tylko chwytać stdout, a nie stderr .
kalaxy
14
Należy również pamiętać, że może wystąpić wyjątek result += buffer, dlatego rura może nie zostać poprawnie zamknięta.
Fred Foo
6
@Yasky: Gdy wykonywany jest program int main(){ puts("ERROR"); }.
dreamlax
8
Odpowiedź jest dobra, ale byłoby lepiej, gdybyś zastąpił „char * cmd” przez „const char * cmd”
fnc12
29
Unique_ptr lepiej tutaj pasuje, gdzie rzeczywista liczba referencji nigdy nie jest używana.
Czipperz
77

Pobieranie zarówno stdout, jak i stderr (a także pisanie do stdin, nie pokazano tutaj) jest łatwe z moim nagłówkiem pstreams , który definiuje klasy iostream, które działają jak 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
źródło
18
Nie zgadzam się. popenwymaga użycia interfejsu API stdio, wolę interfejs API iostreams. popenwymaga ręcznego wyczyszczenia FILEuchwytu, pstreams robi to automatycznie. popenakceptuje tylko const char*argument argumentu, który wymaga ostrożności, aby uniknąć ataków polegających na wstrzyknięciu powłoki, pstreams pozwala przekazać wektor ciągów podobnych do execv, co jest bezpieczniejsze. popendaje ci tylko fajkę, pstreams mówi PID dziecka, pozwalając ci wysyłać sygnały, np. zabić go, jeśli jest zablokowany lub nie wychodzi. Wszystkie te zalety, nawet jeśli chcesz tylko jednokierunkowego We / Wy.
Jonathan Wakely
1
Innym problemem związanym z tym rozwiązaniem jest to, że dziecko zapisuje na stderr wystarczająco, aby wypełnić bufory i zablokować, zanim zacznie pisać na standardowe wyjście. Rodzic zablokuje czytanie standardowego wyjścia, podczas gdy dziecko jest blokowane i oczekuje na odczytanie standardowego standardu. zakleszczenie zasobów! Przynajmniej jedna z tych pętli byłaby lepsza jako asynchroniczna (tzn. Wątkowa).
Jesse Chisholm,
1
@JesseChisholm, tak, to może być problem. Ale nie musisz używać wątków, ponieważ pstreams pozwala na aproksymację nieblokujących I / O za pomocą interfejsu iostream, w szczególności za pomocą funkcji readsome , która sprawdza gotowość pstreambuf::in_avail(), więc nie będzie blokować. Pozwala to na demultipleksowanie na standardowym i standardowym procesie, ponieważ każdy ma dostępne dane. pstreambuf::in_avail()działa tylko w 100% niezawodnie, jeśli system operacyjny obsługuje niestandardowy ioctl FIONREAD, ale jest on obsługiwany w (przynajmniej) GNU / Linux i Solaris.
Jonathan Wakely,
13
@chiliNUT nowa wersja 1.0.1 korzysta z licencji Boost.
Jonathan Wakely
1
@JonathanWakely jak mogę zabić ipstream po upływie 5 sekund?
AK
34

Chciałbym użyć popen () (++ Waqas) .

Ale czasami potrzebujesz czytania i pisania ...

Wygląda na to, że nikt już nie robi trudnych rzeczy.

(Zakładając, że środowisko Unix / Linux / Mac, a może Windows z warstwą kompatybilności z POSIX ...)

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())*/
}

Możesz także pobawić się odczytami select () i nieblokującymi.

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
źródło
1
Ciężka droga jest słuszna :) Podoba mi się pomysł z wywołaniem select (), ale w tym przypadku muszę poczekać, aż zadanie się zakończy. Zachowam ten kod dla innego projektu, który mam :)
Misha M
4
... lub możesz skorzystać z istniejącej funkcji posix_spawnp
Per Johansson
5
Twój execlp wywołaniu jest błąd: ostatni argprzekazany wskaźnik musi (char *) NULLpoprawnie zakończyć listę argumentów variadic (patrz execlp(3)odnośnik).
Kristóf Marussy
Czy to zadziała na systemach Unix, Linux i Windows? Czy możesz również nagłówki plików?
kittu
Gdzie przekazujesz plik .bat w kodzie?
kittu
33

Dla Windowsa, popen również działa, ale otwiera okno konsoli - które szybko miga nad aplikacją interfejsu użytkownika. Jeśli chcesz być profesjonalistą, lepiej wyłączyć to „flashowanie” (szczególnie jeśli użytkownik końcowy może to anulować).

Oto moja własna wersja dla systemu Windows:

(Ten kod jest częściowo rekombinowany z pomysłów napisanych w przykładach The Code Project i MSDN).

#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
źródło
1
To moje ulubione rozwiązanie dla systemu Windows, mam nadzieję, że wybaczysz moje zmiany. Sugeruję, aby uczynić const-cast bardziej wyraźnym, podczas gdy uważam jawne użycie wchar_ti CreateProcessWza niepotrzebne ograniczenie.
Wolf
Czy widzisz jakiś problem lub potencjalny problem z tą obsadą? Wolę ograniczyć kod do minimum i nie pisać go bez potrzeby.
TarmoPikaro
4
Po przeczytaniu funkcji CreateProcess (Windows) widzę w tym poważne niebezpieczeństwo: 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.więc może lepiej najpierw skopiować wiersz poleceń do osobnego bufora, aby zapobiec zmianie pierwotnego wejścia programu wywołującego.
Wolf
Ta odpowiedź nie obsługuje poprawnie stderr.
Refael Sheinker
Czy to działa również w systemach Unix? A może musiałbym użyć czegoś innego dla urządzenia z systemem Unix?
255.tar.xz
17

Dwa możliwe podejścia:

  1. Nie sądzę, że popen()jest częścią standardu C ++ (jest to część POSIX z pamięci), ale jest dostępna w każdym systemie UNIX, z którym pracowałem (i wydaje się, że celujesz w UNIX, ponieważ twoje polecenie jest ./some_command).

  2. Jeśli nie ma takiej możliwości popen(), możesz użyć system("./some_command >/tmp/some_command.out");, a następnie użyć normalnych funkcji we / wy do przetworzenia pliku wyjściowego.

paxdiablo
źródło
Dzięki za popen, na razie będę tego używać i będę się martwić o systemy inne niż POSIX, jeśli to się pojawi.
Misha M
8

Nie mogłem zrozumieć, dlaczego brakuje popen / pclose w Code :: Blocks / MinGW. Więc obejrzałem ten problem, używając zamiast tego CreateProcess () i CreatePipe ().

Oto rozwiązanie, które zadziałało dla mnie:

//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
źródło
5
Dziękuję Ci! To najlepsza popularna implementacja systemu Windows w Internecie! I przekazując flagę CREATE_NO_WINDOW, można w końcu pozbyć się irytujących podpowiedzi cmd, które się pojawiają.
Lacho Tomov
1
Gdzie mijasz coś CREATE_NO_WINDOW?
Refael Sheinker
2
@ Bill Moore, jeśli zauważysz, w odpowiedzi jest błąd. ListStdErrnigdy nie jest używany.
Refael Sheinker
7

Poniższe może być rozwiązaniem przenośnym. Jest zgodny ze standardami.

#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
źródło
4
Dostaję to ostrzeżenie z gcc: „ostrzeżenie: użycie tmpnamjest niebezpieczne, lepsze użycie mkstemp
Mark Lakata
4

Zakładając POSIX, prosty kod do przechwytywania standardowego:

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

Dodatki do kodu są mile widziane dla większej funkcjonalności:

https://github.com/ericcurtin/execxx

ericcurtin
źródło
2

Dane wyjściowe można uzyskać po uruchomieniu skryptu za pomocą potoku. Używamy rur, gdy chcemy uzyskać wynik procesu potomnego.

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

Oto skrypt, który chcesz uruchomić. Umieść go w zmiennej polecenia wraz z argumentami, które pobiera twój skrypt (nic, jeśli nie ma żadnych argumentów). I plik, w którym chcesz przechwycić dane wyjściowe skryptu, umieść je w copy_fp.

Więc popen uruchamia twój skrypt i umieszcza dane wyjściowe w fpipe, a następnie możesz po prostu skopiować wszystko z tego do pliku wyjściowego.

W ten sposób możesz uchwycić wyniki procesów potomnych.

I innym procesem jest to, że możesz bezpośrednio umieścić > operatora tylko w poleceniu. Więc jeśli umieścimy wszystko w pliku podczas wykonywania polecenia, nie będziesz musiał niczego kopiować.

W takim przypadku nie ma potrzeby używania rur. Możesz użyć just system, a to uruchomi polecenie i umieści dane wyjściowe w tym pliku.

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

Możesz przeczytać samouczek YoLinux: Sterowanie widelcem, wykonaniem i procesem, aby uzyskać więcej informacji.

Muskan Agarwal
źródło
1

Zauważ, że możesz uzyskać dane wyjściowe, przekierowując dane wyjściowe do pliku, a następnie czytając je

Pokazano to w dokumentacji std::system

Możesz otrzymać kod wyjścia, wywołując WEXITSTATUSmakro.

    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
źródło