如何在 c 中的 popen() 函数中使用的命令中转义特殊字符?
how to escape special characters in a command that is used in popen() function in c?
我想将系统命令输出存储在 Linux 中的 c 程序的变量中。
在这种情况下,我需要通过 popen() 函数在 c 程序中 运行 一个长的 liner 命令。我有下面的长命令,运行在终端中很好。
awk '{if(l1){print (-l1)/1024"kB/s",(-l2)/1024"kB/s"} else{l1=; l2=;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)
它在终端中打印当前的下载和上传速度。
我有以下原型代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define COMMAND_LEN 128
#define DATA_SIZE 512
int main(int argc,char *argv[]){
FILE *pf;
char command[COMMAND_LEN];
char data[DATA_SIZE];
// Execute a command
sprintf(command, "awk '{if(l1){print (-l1)/1024"kB/s",(-l2)/1024"kB/s"} else{l1=; l2=;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)");
// Setup our pipe for reading and execute our command.
pf = popen(command,"r");
if(!pf){
fprintf(stderr, "Could not open pipe for output.\n");
return;
}
// Grab data from process execution
fgets(data, DATA_SIZE , pf);
// Print grabbed data to the screen.
fprintf(stdout, "-%s-\n",data);
if (pclose(pf) != 0)
fprintf(stderr," Error: Failed to close command stream \n");
return 0;
}
我知道我必须转义反斜杠或双引号等特殊字符。
如果我将 "awk '{if(l1){print (-l1)/1024"kB/s",(-l2)/1024"kB/s"} else{l1=; l2=;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)
" 替换为 "echo \"hello world\""
运行很好。
我的问题是:
如何转义 awk '{if(l1){print (-l1)/1024"kB/s",(-l2)/1024"kB/s"} else{l1=; l2=;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)
中的特殊字符以使其工作?
我尝试了很多组合都没有成功。
编辑:
正如 JoachimPileborg 告诉我只有转义字符是双引号一样,我删除了那些在 linux 命令中无用的双引号,例如
awk '{if(l1){print (-l1)/1024,(-l2)/1024} else{l1=; l2=;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)
但我还是明白了
sh: 1: Syntax error: "(" unexpected
-c/net/dev)-
Error: Failed to close command stream
错误。
编辑2:
Pinetwig 指出该命令的长度为 140 个字节,而 COMMAND_LEN。我分配的值是 124。我将该值增加到 1024,但结果没有改变。
我认为错误与 <
管道方向有关。当我删除它后面的行时,它 运行s 没有错误。我不知道这里发生了什么。
字符串 "awk '{if(l1){print (-l1)/1024,(-l2)/1024} else{l1=; l2=;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)"
似乎被很好地转义了。然而,它是 140 字节长,而缓冲区是 128 字节长。我认为您可能正在覆盖缓冲区。你能试试增加 COMMAND_LEN 吗?
错误信息
sh: 1: Syntax error: "(" unexpected
来自使用 bash(或 ksh)功能:Process Substitution:
<(grep wlan0 /proc/net/dev)
(实际上,出现了两次)。对于 bash,如果您开始一个脚本时没有 告诉 它是 bash,它将避免接受它的大部分扩展——比如这个。要在 popen
命令中解决这个问题,您必须将整个命令包装在 bash -c
中,并在命令本身周围加上另一层引号。
制作一个单独的 shell 脚本可能更简单。
不是答案,而是旁注:
既然可以直接处理 /proc/net/dev
伪文件,为什么还要使用外部命令?它是 Linux 内核提供的用户空间接口,因此在可预见的未来不会以不兼容的方式进行更改。
就我个人而言,我会使用如下内容:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
struct interface {
struct interface *next;
unsigned long long rx_bytes;
unsigned long long rx_packets;
unsigned long long rx_errors;
unsigned long long rx_dropped;
unsigned long long rx_fifo;
unsigned long long rx_frame;
unsigned long long rx_compressed;
unsigned long long rx_multicast;
unsigned long long tx_bytes;
unsigned long long tx_packets;
unsigned long long tx_errors;
unsigned long long tx_dropped;
unsigned long long tx_fifo;
unsigned long long tx_collisions;
unsigned long long tx_carrier;
unsigned long long tx_compressed;
char name[];
};
void free_interfaces(struct interface *next)
{
while (next != NULL) {
struct interface *curr = next;
next = next->next;
curr->next = NULL;
curr->name[0] = '[=10=]';
free(curr);
}
}
struct interface *list_interfaces(void)
{
struct interface *list = NULL;
struct interface *iface;
FILE *in;
unsigned long long field[16];
char *name, *next, *ends;
size_t i, namelen;
char *line = NULL;
size_t size = 0;
ssize_t len;
in = fopen("/proc/net/dev", "rb");
if (in == NULL)
return NULL; /* errno was set by fopen() */
while (1) {
len = getline(&line, &size, in);
if (len < (ssize_t)1)
break;
name = line;
while (*name == ' ')
name++;
ends = name;
while (*ends != '[=10=]' && *ends != '\n' && *ends != ' ' && *ends != ':')
ends++;
if (*ends != ':' || ends == name)
continue;
namelen = (size_t)(ends - name);
next = ends + 1;
for (i = 0; i < 15; i++) {
ends = NULL;
errno = 0;
field[i] = strtoull(next, &ends, 0);
if (ends == NULL || ends == next || errno != 0) {
ends = NULL;
break;
}
next = ends;
}
if (ends == NULL)
continue;
iface = malloc(sizeof (struct interface) + namelen + 1);
if (iface == NULL) {
fclose(in);
free_interfaces(list);
errno = ENOMEM;
return NULL;
}
memcpy(iface->name, name, namelen);
iface->name[namelen] = '[=10=]';
iface->rx_bytes = field[0];
iface->rx_packets = field[1];
iface->rx_errors = field[2];
iface->rx_dropped = field[3];
iface->rx_fifo = field[4];
iface->rx_frame = field[5];
iface->rx_compressed = field[6];
iface->rx_multicast = field[7];
iface->tx_bytes = field[8];
iface->tx_packets = field[9];
iface->tx_errors = field[10];
iface->tx_dropped = field[11];
iface->tx_fifo = field[12];
iface->tx_collisions = field[13];
iface->tx_carrier = field[14];
iface->tx_compressed = field[15];
iface->next = list;
list = iface;
}
free(line);
line = NULL;
if (ferror(in) || !feof(in)) {
fclose(in);
free_interfaces(list);
errno = EIO;
return NULL;
}
if (fclose(in)) {
free_interfaces(list);
errno = EIO;
return NULL;
}
errno = 0;
return list;
}
int main(void) {
struct interface *list, *curr;
list = list_interfaces();
if (!list) {
fprintf(stderr, "Cannot get network interface statistics: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
for (curr = list; curr != NULL; curr = curr->next)
printf("%s: %llu bytes, %llu packets sent; %llu bytes, %llu packets received.\n",
curr->name, curr->tx_bytes, curr->tx_packets, curr->rx_bytes, curr->rx_packets);
free_interfaces(list);
return EXIT_SUCCESS;
}
以下修改示例每五秒输出一次网络传输速率,直到它被中断或终止(通过 INT (Ctrl+C,或 TERM 信号)。
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
struct interface {
struct interface *next;
unsigned long long rx_bytes;
unsigned long long rx_packets;
unsigned long long rx_errors;
unsigned long long rx_dropped;
unsigned long long rx_fifo;
unsigned long long rx_frame;
unsigned long long rx_compressed;
unsigned long long rx_multicast;
unsigned long long tx_bytes;
unsigned long long tx_packets;
unsigned long long tx_errors;
unsigned long long tx_dropped;
unsigned long long tx_fifo;
unsigned long long tx_collisions;
unsigned long long tx_carrier;
unsigned long long tx_compressed;
char name[];
};
static volatile sig_atomic_t done = 0;
static void done_handler(int signum)
{
done = 1;
}
static int install_done(const int signum)
{
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_handler = done_handler;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) != -1)
errno = 0;
return errno;
}
void free_interfaces(struct interface *next)
{
while (next != NULL) {
struct interface *curr = next;
next = next->next;
curr->next = NULL;
curr->name[0] = '[=11=]';
free(curr);
}
}
struct interface *find_interface(struct interface *list, const char *const name)
{
if (!name)
return NULL;
while (list != NULL)
if (!strcmp(list->name, name))
return list;
else
list = list->next;
return NULL;
}
struct interface *list_interfaces(void)
{
struct interface *list = NULL;
struct interface *iface;
FILE *in;
unsigned long long field[16];
char *name, *next, *ends;
size_t i, namelen;
char *line = NULL;
size_t size = 0;
ssize_t len;
in = fopen("/proc/net/dev", "rb");
if (in == NULL)
return NULL; /* errno was set by fopen() */
while (1) {
len = getline(&line, &size, in);
if (len < (ssize_t)1)
break;
name = line;
while (*name == ' ')
name++;
ends = name;
while (*ends != '[=11=]' && *ends != '\n' && *ends != ' ' && *ends != ':')
ends++;
if (*ends != ':' || ends == name)
continue;
namelen = (size_t)(ends - name);
next = ends + 1;
for (i = 0; i < 15; i++) {
ends = NULL;
errno = 0;
field[i] = strtoull(next, &ends, 0);
if (ends == NULL || ends == next || errno != 0) {
ends = NULL;
break;
}
next = ends;
}
if (ends == NULL)
continue;
iface = malloc(sizeof (struct interface) + namelen + 1);
if (iface == NULL) {
fclose(in);
free_interfaces(list);
errno = ENOMEM;
return NULL;
}
memcpy(iface->name, name, namelen);
iface->name[namelen] = '[=11=]';
iface->rx_bytes = field[0];
iface->rx_packets = field[1];
iface->rx_errors = field[2];
iface->rx_dropped = field[3];
iface->rx_fifo = field[4];
iface->rx_frame = field[5];
iface->rx_compressed = field[6];
iface->rx_multicast = field[7];
iface->tx_bytes = field[8];
iface->tx_packets = field[9];
iface->tx_errors = field[10];
iface->tx_dropped = field[11];
iface->tx_fifo = field[12];
iface->tx_collisions = field[13];
iface->tx_carrier = field[14];
iface->tx_compressed = field[15];
iface->next = list;
list = iface;
}
free(line);
line = NULL;
if (ferror(in) || !feof(in)) {
fclose(in);
free_interfaces(list);
errno = EIO;
return NULL;
}
if (fclose(in)) {
free_interfaces(list);
errno = EIO;
return NULL;
}
errno = 0;
return list;
}
static void set_timespec(struct timespec *const ptr, const double seconds)
{
if (ptr) {
if (seconds <= 0.0) {
ptr->tv_sec = 0;
ptr->tv_nsec = 0;
} else {
const long s = (long)seconds;
const long ns = (seconds - (double)s) * 1000000000.0;
ptr->tv_sec = s;
if (ns < 0L)
ptr->tv_nsec = 0L;
else
if (ns < 1000000000L)
ptr->tv_nsec = ns;
else
ptr->tv_nsec = 999999999L;
}
}
}
static double get_timespec(const struct timespec *const ptr)
{
if (ptr)
return (double)ptr->tv_sec + (double)ptr->tv_nsec / 1000000000.0;
else
return 0.0;
}
int main(void) {
struct interface *before, *after;
double interval = 5.0;
if (install_done(SIGINT) || install_done(SIGTERM)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
before = NULL;
after = list_interfaces();
if (!after) {
fprintf(stderr, "Cannot get network interface statistics: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
while (!done) {
struct interface *curr, *prev;
struct timespec req, rem;
double duration = interval;
double tx_rate, rx_rate;
set_timespec(&req, duration);
if (nanosleep(&req, &rem) == -1 && errno == EINTR)
duration -= get_timespec(&rem);
if (done)
break;
if (duration <= 0.0)
continue;
free_interfaces(before);
before = after;
after = list_interfaces();
if (!after) {
fprintf(stderr, "Cannot get network interface statistics: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
rx_rate = 0.0;
tx_rate = 0.0;
for (curr = after; curr != NULL; curr = curr->next) {
if (!strcmp(curr->name, "lo"))
continue;
prev = find_interface(before, curr->name);
if (prev) {
const double rx = ((double)curr->rx_bytes - (double)prev->rx_bytes) * 8.0 / 1024.0 / duration;
const double tx = ((double)curr->tx_bytes - (double)prev->tx_bytes) * 8.0 / 1024.0 / duration;
printf("%s: %9.0f kbits/s sent, %9.0f kbits/s received\n", curr->name, tx, rx);
rx_rate += rx;
tx_rate += tx;
}
}
printf("Total: %9.0f kbits/s sent, %9.0f kbits/s received\n\n", tx_rate, rx_rate);
fflush(stdout);
}
return EXIT_SUCCESS;
}
值得一提的是,这是一个不需要生成各种进程来获取信息的函数版本:
/* feature-test macro needed for asprintf(3) */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* Should be a command-line parameter */
#define COUNT 60
/* Open a statistic file */
FILE* open_stat(const char* iface, const char* stat) {
char* filename = NULL;
FILE* file;
if (asprintf(&filename,
"/sys/class/net/%s/statistics/%s",
iface, stat) < 0) {
perror("asprintf");
}
else {
file = fopen(filename, "r");
if (file)
setvbuf(file, NULL, _IONBF, 0);
else
perror(filename);
}
free(filename);
return file;
}
/* Read a statistic from a statistic file */
unsigned long read_stat(FILE* statfile) {
unsigned long value;
rewind(statfile);
int n = fscanf(statfile, "%lu", &value);
if (n != 1) { perror("scanf"); return -1; }
return value;
}
/* Sample main file */
int main(int argc, char** argv) {
const char* iface = "wlan0";
if (argc > 1) iface = argv[1];
FILE* recvf = open_stat(iface, "rx_bytes");
if (!recvf) exit(1);
FILE* xmitf = open_stat(iface, "tx_bytes");
if (!xmitf) exit(1);
unsigned long recv = read_stat(recvf);
unsigned long xmit = read_stat(xmitf);
for(int i = 0; i < COUNT; ++i) {
sleep(1);
unsigned long new_recv = read_stat(recvf);
unsigned long new_xmit = read_stat(xmitf);
printf("in: %6.3f kB/s, out: %6.3f kB/s\n",
(new_recv - recv) / 1024.0,
(new_xmit - xmit) / 1024.0);
recv = new_recv;
xmit = new_xmit;
}
fclose(recvf);
fclose(xmitf);
return 0;
}
我想将系统命令输出存储在 Linux 中的 c 程序的变量中。 在这种情况下,我需要通过 popen() 函数在 c 程序中 运行 一个长的 liner 命令。我有下面的长命令,运行在终端中很好。
awk '{if(l1){print (-l1)/1024"kB/s",(-l2)/1024"kB/s"} else{l1=; l2=;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)
它在终端中打印当前的下载和上传速度。
我有以下原型代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define COMMAND_LEN 128
#define DATA_SIZE 512
int main(int argc,char *argv[]){
FILE *pf;
char command[COMMAND_LEN];
char data[DATA_SIZE];
// Execute a command
sprintf(command, "awk '{if(l1){print (-l1)/1024"kB/s",(-l2)/1024"kB/s"} else{l1=; l2=;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)");
// Setup our pipe for reading and execute our command.
pf = popen(command,"r");
if(!pf){
fprintf(stderr, "Could not open pipe for output.\n");
return;
}
// Grab data from process execution
fgets(data, DATA_SIZE , pf);
// Print grabbed data to the screen.
fprintf(stdout, "-%s-\n",data);
if (pclose(pf) != 0)
fprintf(stderr," Error: Failed to close command stream \n");
return 0;
}
我知道我必须转义反斜杠或双引号等特殊字符。
如果我将 "awk '{if(l1){print (-l1)/1024"kB/s",(-l2)/1024"kB/s"} else{l1=; l2=;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)
" 替换为 "echo \"hello world\""
运行很好。
我的问题是:
如何转义 awk '{if(l1){print (-l1)/1024"kB/s",(-l2)/1024"kB/s"} else{l1=; l2=;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)
中的特殊字符以使其工作?
我尝试了很多组合都没有成功。
编辑: 正如 JoachimPileborg 告诉我只有转义字符是双引号一样,我删除了那些在 linux 命令中无用的双引号,例如
awk '{if(l1){print (-l1)/1024,(-l2)/1024} else{l1=; l2=;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)
但我还是明白了
sh: 1: Syntax error: "(" unexpected
-c/net/dev)-
Error: Failed to close command stream
错误。
编辑2:
Pinetwig 指出该命令的长度为 140 个字节,而 COMMAND_LEN。我分配的值是 124。我将该值增加到 1024,但结果没有改变。
我认为错误与 <
管道方向有关。当我删除它后面的行时,它 运行s 没有错误。我不知道这里发生了什么。
字符串 "awk '{if(l1){print (-l1)/1024,(-l2)/1024} else{l1=; l2=;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)"
似乎被很好地转义了。然而,它是 140 字节长,而缓冲区是 128 字节长。我认为您可能正在覆盖缓冲区。你能试试增加 COMMAND_LEN 吗?
错误信息
sh: 1: Syntax error: "(" unexpected
来自使用 bash(或 ksh)功能:Process Substitution:
<(grep wlan0 /proc/net/dev)
(实际上,出现了两次)。对于 bash,如果您开始一个脚本时没有 告诉 它是 bash,它将避免接受它的大部分扩展——比如这个。要在 popen
命令中解决这个问题,您必须将整个命令包装在 bash -c
中,并在命令本身周围加上另一层引号。
制作一个单独的 shell 脚本可能更简单。
不是答案,而是旁注:
既然可以直接处理 /proc/net/dev
伪文件,为什么还要使用外部命令?它是 Linux 内核提供的用户空间接口,因此在可预见的未来不会以不兼容的方式进行更改。
就我个人而言,我会使用如下内容:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
struct interface {
struct interface *next;
unsigned long long rx_bytes;
unsigned long long rx_packets;
unsigned long long rx_errors;
unsigned long long rx_dropped;
unsigned long long rx_fifo;
unsigned long long rx_frame;
unsigned long long rx_compressed;
unsigned long long rx_multicast;
unsigned long long tx_bytes;
unsigned long long tx_packets;
unsigned long long tx_errors;
unsigned long long tx_dropped;
unsigned long long tx_fifo;
unsigned long long tx_collisions;
unsigned long long tx_carrier;
unsigned long long tx_compressed;
char name[];
};
void free_interfaces(struct interface *next)
{
while (next != NULL) {
struct interface *curr = next;
next = next->next;
curr->next = NULL;
curr->name[0] = '[=10=]';
free(curr);
}
}
struct interface *list_interfaces(void)
{
struct interface *list = NULL;
struct interface *iface;
FILE *in;
unsigned long long field[16];
char *name, *next, *ends;
size_t i, namelen;
char *line = NULL;
size_t size = 0;
ssize_t len;
in = fopen("/proc/net/dev", "rb");
if (in == NULL)
return NULL; /* errno was set by fopen() */
while (1) {
len = getline(&line, &size, in);
if (len < (ssize_t)1)
break;
name = line;
while (*name == ' ')
name++;
ends = name;
while (*ends != '[=10=]' && *ends != '\n' && *ends != ' ' && *ends != ':')
ends++;
if (*ends != ':' || ends == name)
continue;
namelen = (size_t)(ends - name);
next = ends + 1;
for (i = 0; i < 15; i++) {
ends = NULL;
errno = 0;
field[i] = strtoull(next, &ends, 0);
if (ends == NULL || ends == next || errno != 0) {
ends = NULL;
break;
}
next = ends;
}
if (ends == NULL)
continue;
iface = malloc(sizeof (struct interface) + namelen + 1);
if (iface == NULL) {
fclose(in);
free_interfaces(list);
errno = ENOMEM;
return NULL;
}
memcpy(iface->name, name, namelen);
iface->name[namelen] = '[=10=]';
iface->rx_bytes = field[0];
iface->rx_packets = field[1];
iface->rx_errors = field[2];
iface->rx_dropped = field[3];
iface->rx_fifo = field[4];
iface->rx_frame = field[5];
iface->rx_compressed = field[6];
iface->rx_multicast = field[7];
iface->tx_bytes = field[8];
iface->tx_packets = field[9];
iface->tx_errors = field[10];
iface->tx_dropped = field[11];
iface->tx_fifo = field[12];
iface->tx_collisions = field[13];
iface->tx_carrier = field[14];
iface->tx_compressed = field[15];
iface->next = list;
list = iface;
}
free(line);
line = NULL;
if (ferror(in) || !feof(in)) {
fclose(in);
free_interfaces(list);
errno = EIO;
return NULL;
}
if (fclose(in)) {
free_interfaces(list);
errno = EIO;
return NULL;
}
errno = 0;
return list;
}
int main(void) {
struct interface *list, *curr;
list = list_interfaces();
if (!list) {
fprintf(stderr, "Cannot get network interface statistics: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
for (curr = list; curr != NULL; curr = curr->next)
printf("%s: %llu bytes, %llu packets sent; %llu bytes, %llu packets received.\n",
curr->name, curr->tx_bytes, curr->tx_packets, curr->rx_bytes, curr->rx_packets);
free_interfaces(list);
return EXIT_SUCCESS;
}
以下修改示例每五秒输出一次网络传输速率,直到它被中断或终止(通过 INT (Ctrl+C,或 TERM 信号)。
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
struct interface {
struct interface *next;
unsigned long long rx_bytes;
unsigned long long rx_packets;
unsigned long long rx_errors;
unsigned long long rx_dropped;
unsigned long long rx_fifo;
unsigned long long rx_frame;
unsigned long long rx_compressed;
unsigned long long rx_multicast;
unsigned long long tx_bytes;
unsigned long long tx_packets;
unsigned long long tx_errors;
unsigned long long tx_dropped;
unsigned long long tx_fifo;
unsigned long long tx_collisions;
unsigned long long tx_carrier;
unsigned long long tx_compressed;
char name[];
};
static volatile sig_atomic_t done = 0;
static void done_handler(int signum)
{
done = 1;
}
static int install_done(const int signum)
{
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_handler = done_handler;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) != -1)
errno = 0;
return errno;
}
void free_interfaces(struct interface *next)
{
while (next != NULL) {
struct interface *curr = next;
next = next->next;
curr->next = NULL;
curr->name[0] = '[=11=]';
free(curr);
}
}
struct interface *find_interface(struct interface *list, const char *const name)
{
if (!name)
return NULL;
while (list != NULL)
if (!strcmp(list->name, name))
return list;
else
list = list->next;
return NULL;
}
struct interface *list_interfaces(void)
{
struct interface *list = NULL;
struct interface *iface;
FILE *in;
unsigned long long field[16];
char *name, *next, *ends;
size_t i, namelen;
char *line = NULL;
size_t size = 0;
ssize_t len;
in = fopen("/proc/net/dev", "rb");
if (in == NULL)
return NULL; /* errno was set by fopen() */
while (1) {
len = getline(&line, &size, in);
if (len < (ssize_t)1)
break;
name = line;
while (*name == ' ')
name++;
ends = name;
while (*ends != '[=11=]' && *ends != '\n' && *ends != ' ' && *ends != ':')
ends++;
if (*ends != ':' || ends == name)
continue;
namelen = (size_t)(ends - name);
next = ends + 1;
for (i = 0; i < 15; i++) {
ends = NULL;
errno = 0;
field[i] = strtoull(next, &ends, 0);
if (ends == NULL || ends == next || errno != 0) {
ends = NULL;
break;
}
next = ends;
}
if (ends == NULL)
continue;
iface = malloc(sizeof (struct interface) + namelen + 1);
if (iface == NULL) {
fclose(in);
free_interfaces(list);
errno = ENOMEM;
return NULL;
}
memcpy(iface->name, name, namelen);
iface->name[namelen] = '[=11=]';
iface->rx_bytes = field[0];
iface->rx_packets = field[1];
iface->rx_errors = field[2];
iface->rx_dropped = field[3];
iface->rx_fifo = field[4];
iface->rx_frame = field[5];
iface->rx_compressed = field[6];
iface->rx_multicast = field[7];
iface->tx_bytes = field[8];
iface->tx_packets = field[9];
iface->tx_errors = field[10];
iface->tx_dropped = field[11];
iface->tx_fifo = field[12];
iface->tx_collisions = field[13];
iface->tx_carrier = field[14];
iface->tx_compressed = field[15];
iface->next = list;
list = iface;
}
free(line);
line = NULL;
if (ferror(in) || !feof(in)) {
fclose(in);
free_interfaces(list);
errno = EIO;
return NULL;
}
if (fclose(in)) {
free_interfaces(list);
errno = EIO;
return NULL;
}
errno = 0;
return list;
}
static void set_timespec(struct timespec *const ptr, const double seconds)
{
if (ptr) {
if (seconds <= 0.0) {
ptr->tv_sec = 0;
ptr->tv_nsec = 0;
} else {
const long s = (long)seconds;
const long ns = (seconds - (double)s) * 1000000000.0;
ptr->tv_sec = s;
if (ns < 0L)
ptr->tv_nsec = 0L;
else
if (ns < 1000000000L)
ptr->tv_nsec = ns;
else
ptr->tv_nsec = 999999999L;
}
}
}
static double get_timespec(const struct timespec *const ptr)
{
if (ptr)
return (double)ptr->tv_sec + (double)ptr->tv_nsec / 1000000000.0;
else
return 0.0;
}
int main(void) {
struct interface *before, *after;
double interval = 5.0;
if (install_done(SIGINT) || install_done(SIGTERM)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
before = NULL;
after = list_interfaces();
if (!after) {
fprintf(stderr, "Cannot get network interface statistics: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
while (!done) {
struct interface *curr, *prev;
struct timespec req, rem;
double duration = interval;
double tx_rate, rx_rate;
set_timespec(&req, duration);
if (nanosleep(&req, &rem) == -1 && errno == EINTR)
duration -= get_timespec(&rem);
if (done)
break;
if (duration <= 0.0)
continue;
free_interfaces(before);
before = after;
after = list_interfaces();
if (!after) {
fprintf(stderr, "Cannot get network interface statistics: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
rx_rate = 0.0;
tx_rate = 0.0;
for (curr = after; curr != NULL; curr = curr->next) {
if (!strcmp(curr->name, "lo"))
continue;
prev = find_interface(before, curr->name);
if (prev) {
const double rx = ((double)curr->rx_bytes - (double)prev->rx_bytes) * 8.0 / 1024.0 / duration;
const double tx = ((double)curr->tx_bytes - (double)prev->tx_bytes) * 8.0 / 1024.0 / duration;
printf("%s: %9.0f kbits/s sent, %9.0f kbits/s received\n", curr->name, tx, rx);
rx_rate += rx;
tx_rate += tx;
}
}
printf("Total: %9.0f kbits/s sent, %9.0f kbits/s received\n\n", tx_rate, rx_rate);
fflush(stdout);
}
return EXIT_SUCCESS;
}
值得一提的是,这是一个不需要生成各种进程来获取信息的函数版本:
/* feature-test macro needed for asprintf(3) */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* Should be a command-line parameter */
#define COUNT 60
/* Open a statistic file */
FILE* open_stat(const char* iface, const char* stat) {
char* filename = NULL;
FILE* file;
if (asprintf(&filename,
"/sys/class/net/%s/statistics/%s",
iface, stat) < 0) {
perror("asprintf");
}
else {
file = fopen(filename, "r");
if (file)
setvbuf(file, NULL, _IONBF, 0);
else
perror(filename);
}
free(filename);
return file;
}
/* Read a statistic from a statistic file */
unsigned long read_stat(FILE* statfile) {
unsigned long value;
rewind(statfile);
int n = fscanf(statfile, "%lu", &value);
if (n != 1) { perror("scanf"); return -1; }
return value;
}
/* Sample main file */
int main(int argc, char** argv) {
const char* iface = "wlan0";
if (argc > 1) iface = argv[1];
FILE* recvf = open_stat(iface, "rx_bytes");
if (!recvf) exit(1);
FILE* xmitf = open_stat(iface, "tx_bytes");
if (!xmitf) exit(1);
unsigned long recv = read_stat(recvf);
unsigned long xmit = read_stat(xmitf);
for(int i = 0; i < COUNT; ++i) {
sleep(1);
unsigned long new_recv = read_stat(recvf);
unsigned long new_xmit = read_stat(xmitf);
printf("in: %6.3f kB/s, out: %6.3f kB/s\n",
(new_recv - recv) / 1024.0,
(new_xmit - xmit) / 1024.0);
recv = new_recv;
xmit = new_xmit;
}
fclose(recvf);
fclose(xmitf);
return 0;
}