Frage

Ich habe eine std :: string einen Befehl enthält, mit execv ausgeführt werden, was die beste „C ++“, wie es auf den „char * argv []“ zu konvertieren, die durch den zweiten Parameter von execv erforderlich ist () ?

Zur Klarstellung:

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);
War es hilfreich?

Lösung

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

Natürlich ist dies nicht funktioniert mit Commans, die wie rm "a file.mp3" verwenden zitiert. Sie können die Funktion wordexp welche Sorgen über die POSIX betrachten und vieles mehr.

Andere Tipps

Sehr Nicht-Unixy Antworten hier. Was ist los mit:

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

Warum stören Sie eine Befehlszeile Parser zu schreiben, wenn es gibt einen ganz guten eine bereits auf dem System?

(Hinweis: ein guter Grund ist, weil Sie die Zeichenfolge Sie sind nicht trauen auszuführen Man hofft, dass dies bereits der Fall ist, aber die Schale wird mit dieser Zeichenfolge „mehr“ tun als eine naive Leerzeichen-Splitter. und somit Löcher offen mehr Sicherheit, wenn Sie nicht vorsichtig sind.)

Eine Kombination der c_str () String-Methode und strtok () durch Räume aufteilen sollten Sie das Array von Strings, die Sie brauchen, um exec () übergeben und die damit verbundenen Funktionen.

Vielleicht von Boost.ProgramOptions split_winmain. Boost ist eine gute Wahl in den meisten Fällen. http://www.boost.org/ doc / libs / 1_40_0 / doc / html / program_options / HOWTO.html # id1396212

Wenn Sie nur in Windows interessiert sind (andere Kerne wissen in der Regel nicht über Befehlszeilen in dem Windows-Sinne), können Sie die API-Funktion CommandLineToArgvW verwenden, die die gleichen Konventionen wie die MS C-Laufzeit verwendet wird.

In der Regel kommt es auf dem zitierte Stil der Plattform und / oder die Schale. Das Microsoft C Runtime verwendet einen ganz anderen Stil als z.B. bash!

OK, ich habe Stolpern über das selbst oft genug. Dies ist gerade „C“, so kann er entweder in C oder C ++ eingesteckt werden. Es behandelt einzelne und doppelte Anführungszeichen Strings unterschiedlich. Der Anrufer ist verantwortlich für das Aufheben von Zuweisungen argv [0] (nicht NULL) und 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;
        }
    }
}

Dies ist eine Variation auf litb Antwort, aber ohne all die manuelle Speicherzuweisung. Es wird immer noch nicht damit umgehen zu zitieren.

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

Ich fühle mich irgendwie schmutzig über die const_cast <>, aber Programme sollten wirklich nicht den Inhalt der argv Saiten zu ändern.

Sie können die c_str () -Funktion von std :: string zu konvertieren char *. Die strtok Funktion wird aufgeteilt, um die Zeichenfolge mit den ‚‘ Trennzeichen.

Matt Peitrek LIBTINYC hat ein Modul namens argcargv.cpp, die einen String und analysiert sie aus dem Argument Array unter zitierten Argumente zu berücksichtigen. Beachten Sie, dass es Windows-spezifische, aber es ist ziemlich einfach so sein sollte leicht zu bewegen, was auch immer Plattform, die Sie wollen.

Wenn Sie das tun, ändern sie auch als Parameter die loaction nehmen die Zählung und die einen Zeiger auf das Array argv zu setzen stattdessen externs der Verwendung (nur meine wenig Beratung). Matt brauchte nicht, dass, weil LIBTINYC beträgt die Laufzeit.

Alternativ können Sie Ihre Compiler Runtime Quelle aussehen in (fast alle es zu schaffen), um zu sehen, was sie tun, um die Kommandozeile und entweder Anruf zu analysieren, die direkt (wenn das abwechselnd aus praktikabel sein) oder leihen, um die Ideen von diesem Stück Code.

sein kann, ist es zu spät, um Antwort auf diese Frage, aber man könnte standart POSIX-Funktionen glob oder 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);
}

Es würde drei Parameter vor: -l (long listing-Format), -t (sortiert nach Änderungszeit) und Verzeichnis /etc zur Liste, und führen Sie /bin/ls. Call wordexp() gibt Ihnen genau das gleiche Ergebnis wie /bin/sh -c Aufruf empfohlen vorher aber spawaned Prozess würde Elternprozess nicht /bin/sh hat.

Da es eine Funktion exist stellte sich heraus, etwas für diese im Boost-Programmoptionen versteckt. Die split_unix () Funktion funktioniert mit entkam und Befehlszeilen angegeben.

#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();
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top