2008-09-24 34 views
50

Tôi đang cố bắt đầu ứng dụng bên ngoài thông qua system() - ví dụ: system("ls"). Tôi muốn nắm bắt đầu ra của nó khi nó xảy ra vì vậy tôi có thể gửi nó đến một chức năng khác để xử lý tiếp. Cách tốt nhất để làm điều đó trong C/C++ là gì?Chụp stdout từ lệnh system() tối ưu

+0

Ý của bạn là gì? Từ câu trả lời của tôi, tôi sẽ nói rằng tối ưu có thể phụ thuộc vào từng tình huống. Phương thức fork/exec/dup2/STDOUT_FILENO có thể phù hợp cho các trường hợp đặc biệt. – nephewtom

Trả lời

0

Tôi không hoàn toàn chắc chắn rằng nó có thể trong tiêu chuẩn C, vì hai quy trình khác nhau thường không chia sẻ không gian bộ nhớ. Cách đơn giản nhất mà tôi có thể nghĩ đến là làm chương trình thứ hai chuyển hướng đầu ra của nó sang một tệp văn bản (programname> textfile.txt) và sau đó đọc tệp văn bản đó để xử lý. Tuy nhiên, đó có thể không phải là cách tốt nhất.

6

EDIT: câu hỏi bị đọc sai khi muốn chuyển đầu ra sang chương trình khác, không phải chức năng khác. popen() gần như chắc chắn là những gì bạn muốn.

Hệ thống cung cấp cho bạn quyền truy cập đầy đủ vào trình bao. Nếu bạn muốn tiếp tục sử dụng nó, bạn có thể chuyển hướng đầu ra của nó thành một tệp tạm thời, theo hệ thống ("ls> tempfile.txt"), nhưng việc chọn một tệp tạm thời an toàn là một nỗi đau. Hoặc, bạn thậm chí có thể chuyển hướng nó thông qua một chương trình khác: hệ thống ("ls | otherprogram");

Một số có thể đề xuất lệnh popen(). Đây là những gì bạn muốn nếu bạn có thể tự xử lý đầu ra:

FILE *output = popen("ls", "r"); 

sẽ cung cấp cho bạn một con trỏ FILE mà bạn có thể đọc từ đầu ra của lệnh trên đó. Bạn cũng có thể sử dụng lệnh pipe() để tạo kết nối kết hợp với fork() để tạo các quy trình mới, dup2() để thay đổi đầu vào và đầu ra chuẩn của chúng, exec() để chạy các chương trình mới, và chờ() trong chương trình chính để chờ họ. Đây chỉ là thiết lập các đường ống giống như vỏ sẽ. Xem trang hướng dẫn sử dụng đường ống() để biết chi tiết và ví dụ.

1

Trong Windows, thay vì sử dụng hệ thống(), hãy sử dụng CreateProcess, chuyển hướng đầu ra đến một đường ống và kết nối với đường ống.

Tôi đoán điều này cũng có thể theo một số cách POSIX?

40

Từ hướng dẫn popen:

#include <stdio.h> 

FILE *popen(const char *command, const char *type); 

int pclose(FILE *stream); 
+1

FILE * stream = popen (lệnh const char *, const char * type); – Arpith

+5

Tôi muốn thêm rằng điều này sẽ chỉ hoạt động trên các hệ thống Posix. Trên phiên bản Windows 7 của tôi không có chức năng popen tuy nhiên có một _popen hoạt động. – user1505520

30

Thử popen() chức năng. Nó thực hiện một lệnh, giống như system(), nhưng chỉ đạo đầu ra vào một tệp mới. Một con trỏ tới luồng được trả về.

FILE *lsofFile_p = popen("lsof", "r"); 

    if (!lsofFile_p) 
    { 
    return -1; 
    } 

    char buffer[1024]; 
    char *line_p = fgets(buffer, sizeof(buffer), lsofFile_p); 
    pclose(lsofFile_p); 
1

Các chức năng popen()pclose() có thể là những gì bạn đang tìm kiếm.

Hãy xem glibc manual để biết ví dụ.

3

Các chức năng popen() và như vậy không chuyển hướng tiêu bản và như vậy; Tôi đã viết popen3() cho mục đích đó.

1

Thực ra, tôi chỉ cần kiểm tra, và:

  1. popen là có vấn đề, bởi vì quá trình này được chia hai. Vì vậy, nếu bạn cần phải đợi lệnh shell để thực hiện, thì bạn có nguy cơ bị mất nó. Trong trường hợp của tôi, chương trình của tôi đóng cửa ngay cả trước khi đường ống phải làm việc đó.

  2. Tôi đã kết thúc bằng cách sử dụng hệ thống gọi bằng lệnh tar trên linux. Giá trị trả lại từ hệ thống là kết quả của tar.

Vì vậy: nếu bạn cần giá trị trả về, sau đó không không chỉ là không có nhu cầu sử dụng popen, nó có lẽ sẽ không làm những gì bạn muốn.

+1

Câu hỏi yêu cầu chụp * đầu ra *, không phải * mã thoát *. Vì vậy, tôi không nghĩ rằng vấn đề bạn đề cập đến phát sinh. –

+1

đó là những gì chờ đợi (...) gia đình của các cuộc gọi hệ thống là cho. Họ chờ đợi cho quá trình con của bạn để kết thúc, vì vậy bạn có thể nhận được sản lượng của nó. "người đàn ông -s2 chờ đợi" –

1

Trong trang này: capture_the_output_of_a_child_process_in_c mô tả các hạn chế của việc sử dụng popen so với sử dụng phương pháp fork/exec/dup2/STDOUT_FILENO.

Tôi gặp sự cố capturing tshark output with popen.

Và tôi đoán rằng hạn chế này có thể là vấn đề của tôi:

Nó trả về một dòng stdio như trái ngược với một mô tả tập tin thô, mà là không phù hợp để xử lý đầu ra không đồng bộ.

Tôi sẽ quay lại câu trả lời này nếu tôi có giải pháp với cách tiếp cận khác.

2

Cách hiệu quả nhất là sử dụng stdout mô tả tập tin trực tiếp, bỏ qua FILE dòng:

pid_t popen2(const char *command, int * infp, int * outfp) 
{ 
    int p_stdin[2], p_stdout[2]; 
    pid_t pid; 

    if (pipe(p_stdin) == -1) 
     return -1; 

    if (pipe(p_stdout) == -1) { 
     close(p_stdin[0]); 
     close(p_stdin[1]); 
     return -1; 
    } 

    pid = fork(); 

    if (pid < 0) { 
     close(p_stdin[0]); 
     close(p_stdin[1]); 
     close(p_stdout[0]); 
     close(p_stdout[1]); 
     return pid; 
    } else if (pid == 0) { 
     close(p_stdin[1]); 
     dup2(p_stdin[0], 0); 
     close(p_stdout[0]); 
     dup2(p_stdout[1], 1); 
     dup2(::open("/dev/null", O_WRONLY), 2); 

     /// Close all other descriptors for the safety sake. 
     for (int i = 3; i < 4096; ++i) { 
      ::close(i); 
     } 

     setsid(); 
     execl("/bin/sh", "sh", "-c", command, NULL); 
     _exit(1); 
    } 

    close(p_stdin[0]); 
    close(p_stdout[1]); 

    if (infp == NULL) { 
     close(p_stdin[1]); 
    } else { 
     *infp = p_stdin[1]; 
    } 

    if (outfp == NULL) { 
     close(p_stdout[0]); 
    } else { 
     *outfp = p_stdout[0]; 
    } 

    return pid; 
} 

Để đọc đầu ra từ con sử dụng popen2() như thế này:

int child_stdout = -1; 
pid_t child_pid = popen2("ls", 0, &child_stdout); 

if (!child_pid) { 
    handle_error(); 
} 

char buff[128]; 
ssize_t bytes_read = read(child_stdout, buff, sizeof(buff)); 

Để cả hai ghi và đọc:

int child_stdin = -1; 
int child_stdout = -1; 
pid_t child_pid = popen2("grep 123", &child_stdin, &child_stdout); 

if (!child_pid) { 
    handle_error(); 
} 

const char text = "1\n2\n123\n3"; 
ssize_t bytes_written = write(child_stdin, text, sizeof(text) - 1); 

char buff[128]; 
ssize_t bytes_read = read(child_stdout, buff, sizeof(buff)); 
+1

Là chính xác dup2 (:: mở ("/ dev/null", O_RDONLY), 2) ;? Dường như có nhiều logic hơn (:: mở ("/ dev/null", O_WRONLY), 2); –

+0

@MelVisoMartinez, yup, sẽ hợp lệ hơn. – GreenScape