简体   繁体   中英

how to escape special characters in a command that is used in popen() function in c?

I want to store system command output in a variable in ac program in Linux. In this context I need to run a long liner command in c program via popen() function. I have below long command which runs well in terminal.

    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)

It prints current download and upload speeds in terminal.

I have below prototype code

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

I know that I must escape special characters such as backslashes or double quotes.

If I replace "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) " with "echo \\"hello world\\"" it runs well.

My question is :

How can I escape special characters in 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) to make it work? I tried many combination with no luck.

EDIT: As JoachimPileborg informed me about only escape character is double-quote, I removed those double quotes which is useless in linux command like

  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)

but I still get sh: 1: Syntax error: "(" unexpected -c/net/dev)- Error: Failed to close command stream

error.

EDIT2: Pinetwig pointed out that the command is 140 bytes long while COMMAND_LEN. The value I assigned was 124. I increased that value to 1024, but the result isn't changed. I think the error is related to < pipe direction. When I remove the lines after it, it runs without errors. I don't know what happens here.

The error message

sh: 1: Syntax error: "(" unexpected

comes from using a bash (or ksh) feature: the Process Substitution :

<(grep wlan0 /proc/net/dev)

(actually, two occurrences). For bash, if you begin a script without telling it that it is bash, it will refrain from honoring most of its extensions—such as this one. To work around that in the popen command, you would have to wrap the whole command in a bash -c and another level of quotes around the command itself.

Making that a separate shell script is probably simpler.

Not an answer, but a side note:

Why use an external command, when you could process the /proc/net/dev pseudofile directly? It is an userspace interface provided by the Linux kernel, and as such, not going to change in incompatible ways in the foreseeable future.

Personally, I'd use something like the following:

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

The following modified example outputs the network transfer rates once every five seconds, until it is interrupted or terminated (via INT ( Ctrl + C , or TERM signal).

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

For what it's worth, here's a version of the function which does not need to spawn various processes to get the information:

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

The string "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)" seems well-escaped. However, it is 140 bytes long, while the buffer is 128 bytes long. I think you could be overwriting the buffer. Could you try increasing COMMAND_LEN?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM