在C语言中捕获ping命令的输出

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

当程序完成时,它将同时显示输出。但是我希望在程序执行时立即阅读输出。

使用fork可以解决你的问题。 - MOHAMED
5个回答

8

<stdio.h>默认使用缓冲区,而stdout默认使用行缓冲。

将您的printf("%s", s);替换为printf("%s\n", s);(结尾的换行符会刷新stdout缓冲区),或者在其后添加fflush(NULL);调用。

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

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

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

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


我不认为 printf("%s\n", s); 能解决问题!是吗? - Grijesh Chauhan
1
很遗憾,printf("%s\n", s)和fflush(NULL)对我无效。 - CJD
然后,正如我所建议的那样,执行较低级别的系统调用...并使用poll,因为问题与缓冲有关。 - Basile Starynkevitch

6
以下是使用liboping库每秒ping www.xively.com 并显示延迟的小代码。您可以在Ubuntu上安装静态/动态库文件和头文件,命令如下:sudo apt-get install liboping0 liboping-dev oping 然后使用上述库编译以下程序(gcc -o test test.c -loping)并以超级用户(sudo)身份运行可执行程序。 test.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页面并检查是否成功。


1

你无法立即读取它,也不会在执行结束时打印。

它会在以下时刻显示:

  • 管道缓冲区已满或

  • 管道关闭

你需要修改管道的属性。


1

将您的printf("%s", s);替换为printf("%s\n", s);

\n会刷新缓冲区,因此一旦执行printf命令,您就会立即获得输出,无需等待程序执行停止...


0

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

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

已测试,可正常工作。

其他方法无效,因为问题是管道刷新相关的,而不是当前进程标准输出的刷新。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接