Question

J'ai un std :: chaîne contenant une commande à exécuter avec execv, quel est le meilleur « C ++ » façon de le convertir au « char * argv [] » qui est requis par le deuxième paramètre de execv () ?

Pour préciser:

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);
Était-ce utile?

La solution

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

Bien sûr, cela ne fonctionnera pas avec Commans qui utilisent comme rm "a file.mp3" citant. Vous pouvez considérer la wordexp fonction POSIX qui se soucie de cela et bien plus encore.

Autres conseils

réponses très non-unixy ici. Quel est le problème avec:

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

Pourquoi prendre la peine d'écrire un analyseur de ligne de commande quand il y a un très bon celui déjà sur le système?

(Note: une bonne raison est parce que vous ne faites pas confiance à la chaîne que vous êtes sur le point d'exécuter On espère que cela est déjà vrai, mais la coquille fera « plus » avec cette chaîne d'un séparateur par des espaces naïfs. va et ouvrir ainsi plus de trous de sécurité si vous ne faites pas attention.)

Une combinaison de la méthode de chaîne de c_str () et strtok () pour le diviser par des espaces si vous obtenez le tableau de chaînes dont vous avez besoin pour passer à exec () et ses fonctions connexes.

Peut-être split_winmain de Boost.ProgramOptions. Boost est un bon choix dans la plupart des cas. http://www.boost.org/ doc / libs / 1_40_0 / doc / html / program_options / howto.html # id1396212

Si vous ne souhaitez dans Windows (autres noyaux ne savent généralement pas les lignes de commande dans le sens Windows), vous pouvez utiliser la fonction API CommandLineToArgvW qui utilise les mêmes conventions que le moteur d'exécution MS C.

En général, il dépend du style de citer la plate-forme et / ou la coque. Le Microsoft C Runtime utilise un style tout à fait différent que par exemple bash!

OK, j'ai trébuchant sur ce assez souvent moi-même. Ce droit est « C », de sorte qu'il peut être branché sur C ou C ++. Il traite les chaînes de guillemets simples et doubles différemment. L'appelant est responsable de désaffecter argv [0] (sinon NULL) et 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;
        }
    }
}

Ceci est une variation sur la réponse litb, mais sans toute l'allocation de mémoire manuelle. Il ne peut toujours pas gérer de citer.

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

Je me sens un peu sale sur le const_cast <>, mais les programmes ne devrait vraiment pas être modifier le contenu des chaînes argv.

Vous pouvez utiliser la fonction de std :: string c_str () pour convertir en char *. La fonction strtok va diviser la chaîne en utilisant le délimiteur « ».

Matt Peitrek LIBTINYC a un module appelé argcargv.cpp qui prend une chaîne et l'analyse sur le tableau d'arguments en prenant des arguments cités en compte. Notez qu'il est spécifique à Windows, mais il est assez simple devrait donc être facile de se déplacer à tout plate-forme que vous voulez.

Si vous faites cela, changer aussi de prendre en paramètre le loaction pour mettre le comte et le un pointeur vers le tableau argv au lieu d'utiliser (juste mon externs peu de conseils). Matt n'a pas besoin parce que LIBTINYC était le moteur d'exécution.

Vous pouvez regarder dans (fournir presque tout ce qu'il) source d'exécution de votre compilateur pour voir ce qu'ils font pour analyser la ligne de commande et soit appeler directement (si cela se révèle être réalisable) ou emprunter les idées de ce peu code.

Peut être il est trop tard pour répondre à cette question, mais vous pouvez utiliser les fonctions POSIX standart ou href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/wordexp.html" rel="nofollow noreferrer"> 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);
}

Il préparerait 3 paramètres: -l (format long), la liste -t (tri par date de modification) et le répertoire /etc à la liste, et exécutez /bin/ls. Appel wordexp() vous donne exactement le même résultat que /bin/sh -c d'appel mais le processus précédemment recomended spawaned aurait processus parent non /bin/sh.

En fait une fonction cachée dans un peu existent des options de programme d'encouragement pour cela. La fonction split_unix () fonctionne avec des lignes de commande se sont échappés et cités.

#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();
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top