我想将系统命令输出存储在Linux中的c程序中的变量中。 在这种情况下,我需要通过popen()函数在c程序中运行一个长衬里命令。我有以下长命令,在终端运行良好。
awk '{if(l1){print ($2-l1)/1024"kB/s",($10-l2)/1024"kB/s"} else{l1=$2; l2=$10;}}' <(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 ($2-l1)/1024"kB/s",($10-l2)/1024"kB/s"} else{l1=$2; l2=$10;}}' <(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 ($2-l1)/1024"kB/s",($10-l2)/1024"kB/s"} else{l1=$2; l2=$10;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)
&#34;与"echo \"hello world\""
它运行良好。
我的问题是:
如何在awk '{if(l1){print ($2-l1)/1024"kB/s",($10-l2)/1024"kB/s"} else{l1=$2; l2=$10;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)
中转义特殊字符才能使其正常工作?
我尝试了许多组合而没有运气。
编辑: 正如JoachimPileborg告诉我的那样,只有转义字符是双引号,我删除了那些在linux命令中无效的双引号,如
awk '{if(l1){print ($2-l1)/1024,($10-l2)/1024} else{l1=$2; l2=$10;}}' <(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
错误。
EDIT2:
Pinetwig指出该命令长度为140字节而COMMAND_LEN。我分配的值是124.我将该值增加到1024,但结果没有改变。
我认为错误与<
管道方向有关。当我删除它之后的行时,它运行没有错误。我不知道这里发生了什么。
答案 0 :(得分:1)
错误消息
sh: 1: Syntax error: "(" unexpected
来自使用bash(或ksh)功能: Process Substitution :
<(grep wlan0 /proc/net/dev)
(实际上,两次出现)。对于bash,如果你在没有告诉它的情况下开始一个脚本它是bash,它将不会遵守它的大部分扩展 - 比如这个。要在popen
命令中解决此问题,您必须将整个命令包装在bash -c
中,并在命令本身周围引用另一级别的引号。
使单独的shell脚本可能更简单。
答案 1 :(得分:1)
不是答案,而是旁注:
为什么在可以直接处理/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] = '\0';
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 != '\0' && *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] = '\0';
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] = '\0';
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 != '\0' && *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] = '\0';
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;
}
答案 2 :(得分:1)
对于它的价值,这里有一个函数的版本,它不需要产生各种进程来获取信息:
/* 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;
}
答案 3 :(得分:0)
字符串"awk '{if(l1){print ($2-l1)/1024,($10-l2)/1024} else{l1=$2; l2=$10;}}' <(grep wlan0 /proc/net/dev) <(sleep 1; grep wlan0 /proc/net/dev)"
似乎很容易被转义。但是,它长140个字节,而缓冲区长128个字节。我想你可能会覆盖缓冲区。你能尝试增加COMMAND_LEN吗?