如何转义c语言中popen()函数中使用的命令中的特殊字符



我想在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;
  }

我知道我必须转义特殊字符,如反斜杠或双引号。

如果我用"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)"它运行良好。

我的问题是:

如何在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,但结果没有改变。我认为错误与<管道方向有关。当我删除它后面的行时,它运行没有错误。我不知道这里发生了什么

错误信息

sh: 1: Syntax error: "(" unexpected

来自于使用bash(或ksh)特性:进程替换:

<(grep wlan0 /proc/net/dev)

(实际上,出现两次)。对于bash,如果在开始一个脚本时没有告诉它是bash,那么它将不会执行它的大多数扩展名,比如这个扩展名。要在popen命令中解决这个问题,您必须将整个命令包装在bash -c中,并在命令本身周围加上另一级引号。

值得注意的是,下面是该函数的一个版本,它不需要生成各种进程来获取信息:

/* 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/sn",
           (new_recv - recv) / 1024.0,
           (new_xmit - xmit) / 1024.0);
    recv = new_recv;
    xmit = new_xmit;
  }
  fclose(recvf);
  fclose(xmitf);
  return 0;
}

不是答案,而是旁注:

如果可以直接处理/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] = '';
        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 != '' && *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] = '';
        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] = '';
        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 != '' && *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] = '';
        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 receivedn", curr->name, tx, rx);
                rx_rate += rx;
                tx_rate += tx;
            }
        }
        printf("Total: %9.0f kbits/s sent, %9.0f kbits/s receivednn", tx_rate, rx_rate);
        fflush(stdout);
    }
    return EXIT_SUCCESS;
}

字符串"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吗?

最新更新