7

如何立即通过管道捕获 ping 命令的输出?

这是我的代码:

int main ()
{
    FILE *cmd = popen ( "ping -c 3 google.com | grep icmp", "r" );//ping google
    char *s = malloc ( sizeof ( char ) * 200 );
    while ( 1 )
    {
            fgets ( s, sizeof ( char )*200, cmd );
            printf ( "%s", s);//show outcome
            if ( strstr ( s, "icmp_req=3" ) != 0 )
                    break;
    }
    pclose ( cmd );
    return 0;
}

当程序完成时,它会同时显示输出。但我想在程序执行时立即读取输出。

4

5 回答 5

8

<stdio.h> 默认情况下是缓冲的,并且stdout是行缓冲的。

替换你printf("%s", s);printf("%s\n", s);(结尾换行符会刷新stdout缓冲区)或在它之后添加一个调用fflush(NULL);

实际上,您的问题与 无关ping,但管道已缓冲。

您可以执行较低级别的pipe, fork, dup2,read系统调用并显式管理管道上的缓冲区。然后调用poll可能很有用。

您可以考虑使用像liboping这样的 ICMP pinging 库,或者考虑改为执行 HTTP 请求(使用该wget程序,或者最好使用libcurl;也许一个简单的 HTTPHEAD请求就足够了)。作为一般建议,请避免使用popenor分叉进程system(因为目标计算机上可用的命令可能不同)。

阅读一些好的 Linux 编程书籍,例如http://advancedlinuxprogramming.com/

于 2013-01-22T06:33:04.300 回答
6

这是一个使用liboping 每秒ping www.xively.com 并显示延迟的小代码。您可以像这样在 ubuntu 机器上安装 liboping 静态/动态库文件和头文件:sudo apt-get install liboping0 liboping-dev oping

然后使用上述库 ( gcc -o test test.c -loping) 编译以下程序。并以超级用户 (sudo) 身份运行可执行文件。

测试.c:

 /*
  * 1. install liboping, e.g. `sudo apt-get install liboping0 liboping-dev oping`
  * 2. Compile with -loping, e.g. `gcc -o test test.c -loping`
  * 3. Execute using sudo as super user, e.g. `sudo ./test`
  */

#include <stdlib.h>
#include <stdio.h>
#include <oping.h>

int main(int argc, char **argv) {
    pingobj_t *ping;
    pingobj_iter_t *iter;

    if ((ping = ping_construct()) == NULL) {
        fprintf(stderr, "ping_construct failed\n");
        return -1;
    }
    printf("ping_construct() success\n");

    if (ping_host_add(ping, "www.xively.com") < 0) {
        const char * errmsg = ping_get_error(ping);
        fprintf(stderr, "ping_host_add(www.xively.com) failed. %s\n", errmsg);
        return -1;
    }
    printf("ping_host_add() success\n");

    while (1) {
        if (ping_send(ping) < 0) {
            fprintf(stderr, "ping_send failed\n");
            return -1;
        }
        printf("ping_send() success\n");

        for (iter = ping_iterator_get(ping); iter != NULL; iter =
                ping_iterator_next(iter)) {
            char hostname[100];
            double latency;
            unsigned int len;

            printf("ping_iterator_get() success\n");
            len = 100;
            ping_iterator_get_info(iter, PING_INFO_HOSTNAME, hostname, &len);
            len = sizeof(double);
            ping_iterator_get_info(iter, PING_INFO_LATENCY, &latency, &len);

            printf("hostname = %s, latency = %f\n", hostname, latency);
        }
        sleep(1);
    }
    printf("exiting...\n");

    ping_destroy( ping );

    return 0;
}

输出:

anurag@anurag-PC:~$ sudo ./test
ping_construct() success
ping_host_add() success
ping_send() success
ping_iterator_get() success
hostname = www.xively.com, latency = 233.666000
ping_send() success
ping_iterator_get() success
hostname = www.xively.com, latency = 234.360000
ping_send() success
ping_iterator_get() success
hostname = www.xively.com, latency = 234.076000
ping_send() success
ping_iterator_get() success
hostname = www.xively.com, latency = 231.761000
ping_send() success
ping_iterator_get() success
hostname = www.xively.com, latency = 235.085000
^C

如果您想从 linux 设备检查互联网连接,只要您的 ISP 或目标不阻止 ICMP 数据包,liboping 就很好。如果这些被阻止,您可以使用一些 HTTP 库尝试从 www.google.com 或任何其他网站获取 index.html 页面并检查是否成功

于 2013-07-24T07:32:53.363 回答
1

替换你printf("%s", s);printf("%s\n", s);

\n将刷新您的缓冲区,因此您将在命令执行后立即获得输出,printf而您无需等到程序执行停止...

于 2013-01-22T06:38:14.890 回答
1

您无法立即阅读它,并且它不会在执行结束时打印。

显示的那一刻

  • 管道的缓冲区被填满或

  • 管道关闭

您需要修改管道的属性。

于 2013-01-22T06:35:46.900 回答
0

只需强制换行输出,然后stdout刷新,更改 cmd

char *cmds = "ping -c 3 google.com | awk ' /icmp/ { printf(\"%s\\n\", $0); } '";
FILE *cmd = popen ( cmds, "r" );

有效,我自己测试过。

其他方法不起作用,因为问题在于管道冲洗,而不是当前进程的标准输出冲洗。

于 2013-01-22T09:12:48.143 回答