Pregunta

He un std :: string que contiene un comando que se ejecuta con execv, ¿cuál es la mejor forma de "C ++" para convertirlo en el "char * argv []" que es requerido por el segundo parámetro de execv () ?

Para aclarar:

std::string cmd = "mycommand arg1 arg2";
char *cmd_argv[];

StrToArgv(cmd, cmd_argv); // how do I write this function?

execv(cmd_argv[0], cmd_argv);
¿Fue útil?

Solución

std::vector<char *> args;
std::istringstream iss(cmd);

std::string token;
while(iss >> token) {
  char *arg = new char[token.size() + 1];
  copy(token.begin(), token.end(), arg);
  arg[token.size()] = '\0';
  args.push_back(arg);
}
args.push_back(0);

// now exec with &args[0], and then:

for(size_t i = 0; i < args.size(); i++)
  delete[] args[i];

Por supuesto, esto no funcionará con Commans que utilizan citando como rm "a file.mp3". Se puede considerar la wordexp función POSIX, que se preocupa por eso y mucho más.

Otros consejos

Muy respuestas no unixy aquí. ¿Qué hay de malo en:

std::string cmd = "echo hello world";
execl("/bin/sh", "/bin/sh", "-c", cmd.c_str(), NULL);

¿Por qué molestarse en escribir un analizador de línea de comandos cuando hay un perfectamente bueno que ya están en el sistema?

(Nota: una buena razón es porque no confía en la cadena que está a punto de ejecutar Se espera que esto ya es cierto, pero la concha va a hacer "más" con esa cadena que una ingenua por espacios en blanco divisor. y por lo tanto va a abrir más agujeros de seguridad si no se tiene cuidado.)

Una combinación de la c_str () método de cuerda y strtok () para que se dividió por espacios deben conseguir que la matriz de cadenas que necesita para pasar a exec () y sus funciones relacionadas.

Tal vez split_winmain de Boost.ProgramOptions. Boost es una buena opción en la mayoría de los casos. http://www.boost.org/ doc / libs / 1_40_0 / doc / html / program_options / HOWTO.html # id1396212

Si usted está interesado sólo en Windows (otros granos en general, no saben de líneas de comandos en el sentido de Windows), puede utilizar la función API CommandLineToArgvW que utiliza las mismas convenciones que el tiempo de ejecución de MS C.

En general, depende del estilo citando de la plataforma y / o la cáscara. El Microsoft C Runtime utiliza un estilo bastante diferente de, por ejemplo, fiesta!

OK, he estado dando tumbos por este mismo número suficiente de veces. Este es recta "C", por lo que puede ser enchufado en C o C ++. Se trata cuerdas individuales y dobles comillas diferente. La persona que llama es responsable de cancelar la asignación argv [0] (si no es NULL) y argv.

#include 
#include 
#include 
#include 

typedef enum {
    STR2AV_OK       = 0,
    STR2AV_UNBALANCED_QUOTE
} str_to_argv_err_t;

#ifndef NUL
#define NUL '\0'
#endif

static char const nomem[] = "no memory for %d byte allocation\n";

static str_to_argv_err_t
copy_raw_string(char ** dest_p, char ** src_p);

static str_to_argv_err_t
copy_cooked_string(char ** dest_p, char ** src_p);

static inline void *
Xmalloc(size_t sz)
{
    void * res = malloc(sz);
    if (res == NULL) {
        fprintf(stderr, nomem, sz);
        exit(EXIT_FAILURE);
    }
    return res;
}

static inline void *
Xrealloc(void * ptr, size_t sz)
{
    void * res = realloc(ptr, sz);
    if (res == NULL) {
        fprintf(stderr, nomem, sz);
        exit(EXIT_FAILURE);
    }
    return res;
}

str_to_argv_err_t
string_to_argv(char const * str, int * argc_p, char *** argv_p)
{
    int     argc = 0;
    int     act  = 10;
    char ** res  = Xmalloc(sizeof(char *) * 10);
    char ** argv = res;
    char *  scan;
    char *  dest;
    str_to_argv_err_t err;

    while (isspace((unsigned char)*str))  str++;
    str = scan = strdup(str);

    for (;;) {
        while (isspace((unsigned char)*scan))  scan++;
        if (*scan == NUL)
            break;

        if (++argc >= act) {
            act += act / 2;
            res  = Xrealloc(res, act * sizeof(char *));
            argv = res + (argc - 1);
        }

        *(argv++) = dest = scan;

        for (;;) {
            char ch = *(scan++);
            switch (ch) {
            case NUL:
                goto done;

            case '\\':
                if ( (*(dest++) = *(scan++)) == NUL)
                    goto done;
                break;

            case '\'':
                err = copy_raw_string(&dest, &scan);
                if (err != STR2AV_OK)
                    goto error_leave;
                break;

            case '"':
                err = copy_cooked_string(&dest, &scan);
                if (err != STR2AV_OK)
                    goto error_leave;
                break;

            case ' ':
            case '\t':
            case '\n':
            case '\f':
            case '\r':
            case '\v':
            case '\b':
                goto token_done;

            default:
                *(dest++) = ch;
            }
        }

    token_done:
        *dest = NUL;
    }

done:

    *argv_p = res;
    *argc_p = argc;
    *argv   = NULL;
    if (argc == 0)
        free((void *)str);

    return STR2AV_OK;

error_leave:

    free(res);
    free((void *)str);
    return err;
}

static str_to_argv_err_t
copy_raw_string(char ** dest_p, char ** src_p)
{
    for (;;) {
        char ch = *((*src_p)++);

        switch (ch) {
        case NUL: return STR2AV_UNBALANCED_QUOTE;
        case '\'':
            *(*dest_p) = NUL;
            return STR2AV_OK;

        case '\\':
            ch = *((*src_p)++);
            switch (ch) {
            case NUL:
                return STR2AV_UNBALANCED_QUOTE;

            default:
                /*
                 * unknown/invalid escape.  Copy escape character.
                 */
                *((*dest_p)++) = '\\';
                break;

            case '\\':
            case '\'':
                break;
            }
            /* FALLTHROUGH */

        default:
            *((*dest_p)++) = ch;
            break;
        }
    }
}

static char
escape_convt(char ** src_p)
{
    char ch = *((*src_p)++);

    /*
     *  Escape character is always eaten.  The next character is sometimes
     *  treated specially.
     */
    switch (ch) {
    case 'a': ch = '\a'; break;
    case 'b': ch = '\b'; break;
    case 't': ch = '\t'; break;
    case 'n': ch = '\n'; break;
    case 'v': ch = '\v'; break;
    case 'f': ch = '\f'; break;
    case 'r': ch = '\r'; break;
    }

    return ch;
}


static str_to_argv_err_t
copy_cooked_string(char ** dest_p, char ** src_p)
{
    for (;;) {
        char ch = *((*src_p)++);
        switch (ch) {
        case NUL: return STR2AV_UNBALANCED_QUOTE;
        case '"':
            *(*dest_p) = NUL;
            return STR2AV_OK;

        case '\\':
            ch = escape_convt(src_p);
            if (ch == NUL)
                return STR2AV_UNBALANCED_QUOTE;
            /* FALLTHROUGH */

        default:
            *((*dest_p)++) = ch;
            break;
        }
    }
}

Esta es una variación de la respuesta de litb, pero sin todo el manual de asignación de memoria. Todavía no se encargará de citar.

#include <vector>
#include <string>
#include <sstream>

std::string cmd = "mycommand arg1 arg2";
std::istringstream ss(cmd);
std::string arg;
std::list<std::string> ls;
std::vector<char*> v;
while (ss >> arg)
{
   ls.push_back(arg); 
   v.push_back(const_cast<char*>(ls.back().c_str()));
}
v.push_back(0);  // need terminating null pointer

execv(v[0], &v[0]);

Me siento un poco sucia de la const_cast <>, pero los programas en realidad no debería ser modificar el contenido de las cadenas argv.

Puede utilizar la función c_str () de std :: string para convertir a char *. La función strtok será dividir la cadena utilizando el '' delimitador.

LIBTINYC tiene una llamada argcargv.cpp módulo que toma una cadena y lo analiza a la matriz de argumentos citados teniendo en cuenta los argumentos. Tenga en cuenta que se trata de Windows específica, pero es bastante simple, así debe ser fácil de mover a cualquier plataforma que desee.

Si lo hace, también cambiarlo a tomar como parámetros el loaction para poner el conteo y la de un puntero al vector argv en lugar de utilizar los externos (sólo mi poco de asesoramiento). Matt no necesitaba que debido LIBTINYC fue el tiempo de ejecución.

Como alternativa, se puede ver en la fuente de tiempo de ejecución de su compilador (casi todos proporcionarla) para ver lo que hacen para analizar la línea de comandos y, o bien llamar a eso directamente (si es que resulta ser viable) o pedir prestado las ideas de esa parte de la código.

Puede ser que es demasiado tarde para responder a esta pregunta, pero usted podría utilizar funciones POSIX standart glob o wordexp :

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <wordexp.h>

int
main(int argc, char **argv)
{
   wordexp_t p;
   char *exec_path = "/bin/ls";

   p.we_offs = 1;
   wordexp("-l -t /etc", &p, WRDE_DOOFFS);
   p.we_wordv[ 0 ] = exec_path;
   execv(exec_path, p.we_wordv);

   /* This code is unreachable */
   exit(EXIT_SUCCESS);
}

Se prepararía 3 parámetros: (formato extenso listado -l), -t (ordenar por fecha de modificación) y /etc directorio a la lista, y ejecutar /bin/ls. Llamar wordexp() le da exactamente el mismo resultado que /bin/sh -c llamada recomendado anteriormente pero spawaned proceso tendría proceso padre no /bin/sh.

Al final resultó que una función existir algo escondido en las opciones del programa de este impulso. La función split_unix () trabaja con líneas de comandos escapado y citados.

#include "boost/program_options/parsers.hpp"


auto parts = boost::program_options::split_unix(commandLine);
std::vector<char*> cstrings ;
for(auto& str : parts){
    cstrings.push_back(const_cast<char*> (str.c_str()));
}

int argc = (int)cstrings.size();
char** argv = cstrings.data();
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top