Domanda

Ho un std::string contenente un comando da eseguire con execv, qual è il modo migliore "C++" per convertirlo nel "char *argv[]" richiesto dal secondo parametro di execv()?

Chiarire:

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);
È stato utile?

Soluzione

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];

Naturalmente, questo non funzionerà con commans che utilizzano citando come rm "a file.mp3". Si può considerare la funzione wordexp POSIX che si occupa di questo e molto di più.

Altri suggerimenti

risposte molto non Unixy qui. Cosa c'è di sbagliato:

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

Perché preoccuparsi di scrivere un parser riga di comando quando c'è una perfetta buona già nel sistema?

(Nota: una buona ragione è perché non ti fidi la stringa che si sta per eseguire Si spera che questo è già vero, ma la shell fare "di più" con quella stringa di un ingenuo spazi-splitter. si e quindi aprire più buchi di sicurezza, se non si sta attenti.)

Una combinazione dei c_str metodo e strtok () () stringa di dividerlo da spazi dovrebbe ottenere l'array di stringhe che è necessario passare ad exec () e le relative funzioni.

Forse split_winmain da Boost.ProgramOptions. Boost è una buona scelta nella maggior parte dei casi. http://www.boost.org/ doc / libs / 1_40_0 / doc / html / program_options / HOWTO.html # id1396212

Se si è interessati solo in Windows (altri kernel generalmente non conoscono righe di comando nel senso di Windows), è possibile utilizzare la funzione API CommandLineToArgvW che utilizza le stesse convenzioni come il runtime MS C.

In generale, dipende dallo stile citando della piattaforma e / o Shell. Il Microsoft C Runtime utilizza uno stile molto diverso da quello per esempio bash!

OK, ho inciampare su questo me stesso abbastanza volte. Questo è diritta "C", in modo che possa essere collegato a C o C ++. Si tratta corde singole e doppie virgolette in modo diverso. Il chiamante è responsabile per deallocare argv [0] (se non NULL) e 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;
        }
    }
}

Questa è una variante sulla risposta litb, ma senza tutta l'allocazione di memoria manuale. Ancora non si occuperà citando.

#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]);

Mi sento tipo di sporco sulla const_cast <>, ma i programmi in realtà non dovrebbe essere di modificare il contenuto delle stringhe argv.

È possibile utilizzare la funzione c_str () di std :: string per la conversione a char *. La funzione strtok dividerà la stringa utilizzando il '' delimitatore.

Quello di Matt Peitrek LIBTINICA ha un modulo chiamato argcargv.cpp che prende una stringa e la analizza nell'array di argomenti tenendo conto degli argomenti tra virgolette.Tieni presente che è specifico per Windows, ma è piuttosto semplice, quindi dovrebbe essere facile spostarlo su qualsiasi piattaforma desideri.

Se lo fai, cambialo anche per prendere come parametri la loazione per inserire il conteggio e il puntatore a all'array argv invece di usare externs (solo il mio piccolo consiglio).Matt non ne aveva bisogno perché LIBTINYC era il tempo di esecuzione.

In alternativa, puoi cercare nel sorgente runtime del tuo compilatore (quasi tutti lo forniscono) per vedere cosa fanno per analizzare la riga di comando e chiamarlo direttamente (se risulta fattibile) o prendere in prestito le idee da quel pezzo di codice.

Forse è troppo tardi per rispondere a questa domanda, ma è possibile utilizzare le funzioni 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);
}

Si preparava 3 parametri: (formato lungo messa in vendita) -l, -t (Ordina per data di modifica) e directory /etc di elencare ed eseguire /bin/ls. Chiamata wordexp() ti dà esattamente lo stesso risultato di chiamata /bin/sh -c consigliato in precedenza, ma processo spawaned avrebbe processo genitore non /bin/sh.

Come si è scoperto una funzione esistono un po 'nascosta nelle opzioni del programma spinta per questo. La funzione split_unix () funziona con righe di comando sfuggiti e citati.

#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();
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top