如何在 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;
}