Làm cách nào để thực thi lệnh và nhận đầu ra của lệnh trong C ++ bằng POSIX?


463

Tôi đang tìm cách để có được đầu ra của một lệnh khi nó được chạy từ bên trong một chương trình C ++. Tôi đã xem xét việc sử dụng system()hàm, nhưng điều đó sẽ thực thi một lệnh. Đây là một ví dụ về những gì tôi đang tìm kiếm:

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

Tôi cần chạy một lệnh tùy ý và nhận đầu ra của nó. Tôi đã xem boost.org , nhưng tôi chưa tìm thấy thứ gì sẽ cung cấp cho tôi thứ tôi cần.


Cũng xem câu trả lời trong câu hỏi này: /programming/52164723/how-to-execute-a-command-and-get-return-code-stdout-and-stderr-of-command-in-cđể có phần mở rộng của câu trả lời tuyệt vời bên dưới cung cấp các phương thức để có được return codestderrcũng như stdoutcâu trả lời này đã giải thích
code_fodder

2
@code_fodder bạn có thể tạo một liên kết đến stackoverflow.com/questions/52164723/NH
Jonas Stein

Câu trả lời:


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

Phiên bản Pre-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;
}

Thay thế popenpclosebằng _popen_pclosecho Windows.


69
Hãy lưu ý rằng điều này sẽ chỉ lấy stdout và không stderr .
kal Wax

14
Cũng cần lưu ý rằng một ngoại lệ có thể xảy ra result += buffer, vì vậy đường ống có thể không được đóng lại đúng cách.
Fred Foo

6
@Yasky: Khi chương trình được thực thi là int main(){ puts("ERROR"); }.
dreamlax

8
Câu trả lời là tốt nhưng sẽ tốt hơn nếu bạn thay thế 'char * cmd' bằng 'const char * cmd'
fnc12

29
unique_ptr phù hợp hơn ở đây, trong đó số tham chiếu thực tế không bao giờ được sử dụng.
Czipperz

77

Bắt cả stdout và stderr (và cả viết cho stdin, không hiển thị ở đây) rất dễ dàng với tiêu đề dòng của tôi , định nghĩa các lớp iostream hoạt động như popensau:

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

18
Tôi không đồng ý. popenyêu cầu bạn sử dụng API C stdio, tôi thích API iostreams hơn. popenyêu cầu bạn tự làm sạch FILEtay cầm, dòng chảy tự động làm điều đó. popenchỉ chấp nhận một const char*đối số, yêu cầu cẩn thận để tránh các cuộc tấn công tiêm vỏ, các luồng cho phép bạn vượt qua một vectơ chuỗi tương tự execv, an toàn hơn. popenkhông cung cấp cho bạn gì ngoài một đường ống, dòng chảy cho bạn biết PID của trẻ cho phép bạn gửi tín hiệu, ví dụ như để giết nó nếu nó bị chặn hoặc không thoát. Tất cả những điều đó là lợi thế ngay cả khi bạn chỉ muốn IO đơn hướng.
Jonathan Wakely

1
Một vấn đề khác với giải pháp này là nếu đứa trẻ viết vào stderr đủ để lấp đầy bộ đệm và chặn trước khi nó bắt đầu ghi vào thiết bị xuất chuẩn. Phụ huynh sẽ chặn thiết bị xuất chuẩn, trong khi đứa trẻ bị chặn chờ đọc stderr. bế tắc tài nguyên! Ít nhất một trong những vòng lặp đó sẽ tốt hơn là không đồng bộ (nghĩa là luồng).
Jesse Chisholm

1
@JlieChisholm, vâng, đó có thể là một vấn đề. Nhưng bạn không cần phải sử dụng đề vì pstreams cho phép một xấp xỉ của non-blocking I / O sử dụng giao diện iostream, cụ thể bằng cách sử dụng readsome chức năng, trong đó kiểm tra cho sự sẵn sàng sử dụng pstreambuf::in_avail(), như vậy sẽ không chặn. Điều đó cho phép phân tích trên thiết bị xuất chuẩn và thiết bị xuất chuẩn vì mỗi thiết bị đều có sẵn dữ liệu. pstreambuf::in_avail()chỉ hoạt động đáng tin cậy 100% nếu HĐH hỗ trợ ioctl FIONREAD không chuẩn, nhưng điều đó được hỗ trợ trên (ít nhất là) GNU / Linux và Solaris.
Jonathan Wakely

13
@chiliNUT bản phát hành 1.0.1 mới sử dụng giấy phép Boost.
Jonathan Wakely

1
@JonathanWakely làm thế nào tôi có thể giết ipux sau khi hết thời gian chờ 5 giây?
AK

34

Tôi sẽ sử dụng popen () (++ waqas) .

Nhưng đôi khi bạn cần đọc và viết ...

Có vẻ như không ai làm mọi thứ một cách khó khăn nữa.

(Giả sử môi trường Unix / Linux / Mac, hoặc có lẽ Windows có lớp tương thích 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())*/
}

Bạn cũng có thể muốn chơi xung quanh với các lần đọc select () và không chặn.

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

1
Cách khó là đúng :) Tôi thích ý tưởng với lệnh gọi (), mặc dù trong trường hợp này, tôi thực sự cần phải đợi cho đến khi tác vụ hoàn thành. Tôi sẽ giữ mã này cho một dự án khác mà tôi có :)
Misha M

4
... hoặc bạn có thể sử dụng hàm posix_spawnp hiện có
Per Johansson

5
Cuộc execlpgọi của bạn có một lỗi: argcon trỏ cuối cùng được thông qua phải (char *) NULLchấm dứt chính xác danh sách đối số kiểu chữ (xem execlp(3)để tham khảo).
Kristóf Marussy

Điều này sẽ làm việc trên unix, linux và windows? Bạn có thể vui lòng tiêu đề tập tin là tốt?
kittu

Bạn đang chuyển tập tin .bat trong mã ở đâu?
kittu

33

Đối với Windows, popencũng hoạt động, nhưng nó sẽ mở ra một cửa sổ giao diện điều khiển - nó sẽ nhanh chóng lướt qua ứng dụng UI của bạn. Nếu bạn muốn trở thành một người chuyên nghiệp, tốt hơn hết là tắt "nhấp nháy" này (đặc biệt là nếu người dùng cuối có thể hủy bỏ nó).

Vì vậy, đây là phiên bản của riêng tôi cho Windows:

(Mã này được kết hợp lại một phần từ các ý tưởng được viết trong các mẫu Dự án mã và 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

1
Đây là giải pháp yêu thích của tôi cho Windows, tôi hy vọng bạn tha thứ cho những thay đổi của tôi. Tôi đề nghị làm cho các diễn viên rõ ràng hơn, trong khi tôi coi việc sử dụng rõ ràng wchar_tCreateProcessWnhư là một hạn chế không cần thiết.
Sói

Bạn có thấy bất kỳ vấn đề hoặc vấn đề tiềm năng với dàn diễn viên này không? Tôi thích giữ mã tối thiểu và không viết nó mà không cần.
TarmoPikaro

4
Sau khi đọc chức năng CreatProcess (Windows) , tôi thấy một mối nguy hiểm thực sự khi làm điều này: 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.Vì vậy, tốt hơn hết là sao chép dòng lệnh vào một bộ đệm riêng trước, để ngăn người gọi thay đổi đầu vào ban đầu.
Sói

Câu trả lời này không xử lý stderr đúng cách.
Refael Sheinker

Điều này cũng làm việc cho các hệ thống Unix? Hoặc tôi sẽ phải sử dụng một cái gì đó khác cho một thiết bị Unix?
255.tar.xz

17

Hai cách tiếp cận có thể:

  1. Tôi không nghĩ popen()là một phần của tiêu chuẩn C ++ (nó là một phần của POSIX từ bộ nhớ), nhưng nó có sẵn trên mọi UNIX mà tôi đã làm việc (và dường như bạn đang nhắm mục tiêu UNIX vì lệnh của bạn là ./some_command).

  2. Nếu không có khả năng popen(), bạn có thể sử dụng system("./some_command >/tmp/some_command.out");, sau đó sử dụng các hàm I / O bình thường để xử lý tệp đầu ra.


Cảm ơn popen, tôi sẽ sử dụng nó ngay bây giờ và tôi sẽ lo lắng về các hệ thống không phải POSIX nếu điều đó xuất hiện.
Misha M

8

Tôi không thể hiểu tại sao popen / pclose bị thiếu trong Code :: Blocks / MinGW. Vì vậy, tôi đã giải quyết vấn đề bằng cách sử dụng CreatProcess () và CreatPipe ().

Đây là giải pháp hiệu quả với tôi:

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

5
Cảm ơn bạn! Đây là triển khai popen tốt nhất cho Windows trên Internet! Và bằng cách chuyển cờ CREATE_NO_WINDOW, người ta cuối cùng cũng có thể thoát khỏi các lời nhắc cmd gây phiền nhiễu xuất hiện.
Lacho Tomov

1
Nơi nào bạn vượt qua điều CREATE_NO_WINDOW?
Refael Sheinker

2
@Bill Moore, nếu bạn chú ý, có một lỗi trong câu trả lời của bạn. ListStdErrkhông bao giờ được sử dụng.
Refael Sheinker

7

Sau đây có thể là một giải pháp di động. Nó tuân theo các tiêu chuẩn.

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

4
Tôi nhận được cảnh báo này với gcc: "cảnh báo: việc sử dụng tmpnamlà nguy hiểm, sử dụng tốt hơn mkstemp"
Mark Lakata

4

Giả sử POSIX, mã đơn giản để chụp 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;
}

Đóng góp mã được hoan nghênh cho nhiều chức năng hơn:

https://github.com/ericcurtin/execxx


2

Bạn có thể nhận được đầu ra sau khi chạy tập lệnh bằng cách sử dụng một đường ống. Chúng tôi sử dụng đường ống khi chúng tôi muốn đầu ra của quá trình con.

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

Vì vậy, đây là kịch bản, mà bạn muốn chạy. Đặt nó trong một biến lệnh với các đối số mà kịch bản của bạn mất (không có gì nếu không có đối số). Và tệp mà bạn muốn chụp đầu ra của tập lệnh, đặt nó vào copy_fp.

Vì vậy, popen chạy tập lệnh của bạn và đặt đầu ra trong fpipe và sau đó bạn có thể sao chép mọi thứ từ đó vào tệp đầu ra của mình.

Bằng cách này, bạn có thể nắm bắt được kết quả đầu ra của các tiến trình con.

Và một quá trình khác là bạn có thể trực tiếp đưa >toán tử vào lệnh. Vì vậy, nếu chúng tôi sẽ đặt mọi thứ vào một tệp trong khi chúng tôi chạy lệnh, bạn sẽ không phải sao chép bất cứ điều gì.

Trong trường hợp đó, không cần sử dụng đường ống. Bạn có thể sử dụng chỉ system, và nó sẽ chạy lệnh và đặt đầu ra trong tệp đó.

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

Bạn có thể đọc Hướng dẫn YoLinux: Điều khiển Fork, Exec và Process để biết thêm thông tin.


1

Lưu ý rằng bạn có thể nhận đầu ra bằng cách chuyển hướng đầu ra sang tệp và sau đó đọc nó

Nó đã được hiển thị trong tài liệu của std::system

Bạn có thể nhận mã thoát bằng cách gọi WEXITSTATUSmacro.

    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;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.