Вопрос

У меня есть std::string, содержащий команду, которая должна быть выполнена с помощью execv, каков наилучший способ "C ++" преобразовать ее в "char * argv[]", который требуется вторым параметром execv()?

Чтобы прояснить:

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);
Это было полезно?

Решение

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

Конечно, это не будет работать с запятыми, которые используют кавычки типа rm "a file.mp3".Вы можете рассмотреть функцию POSIX wordexp который заботится об этом и о многом другом.

Другие советы

Здесь даны ответы, очень не похожие на unixy.Что плохого в:

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

Зачем утруждать себя написанием анализатора командной строки, когда в системе уже есть совершенно хороший анализатор?

(Примечание:одна из веских причин заключается в том, что вы не доверяете строке, которую собираетесь выполнить.Можно надеяться, что это уже верно, но оболочка сделает "больше" с этой строкой, чем наивный разделитель пробелов, и, таким образом, откроет больше дыр в безопасности, если вы не будете осторожны.)

Комбинация строкового метода c_str() и strtok() для разделения его пробелами должна дать вам массив строк, который вам нужно передать в exec() и связанные с ним функции.

Возможно split_winmain из Boost.Варианты программы.Boost - хороший выбор в большинстве случаев.http://www.boost.org/doc/libs/1_40_0/doc/html/program_options/howto.html#id1396212

Если вас интересует только Windows (другие ядра обычно не знают о командных строках в смысле Windows), вы можете использовать функцию API CommandLineToArgvW который использует те же соглашения, что и среда выполнения MS C.

В общем, это зависит от стиля цитирования платформы и / или оболочки.Среда выполнения Microsoft C использует совершенно иной стиль, чем, например,удар!

Ладно, я сам достаточно раз спотыкался об это.Это прямой "C", поэтому его можно подключить либо к C, либо к C ++.Он по-разному обрабатывает строки в одинарных и двойных кавычках.Вызывающий объект отвечает за освобождение argv[0] (если не NULL) и 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;
        }
    }
}

Это вариант ответа litb, но без всего ручного выделения памяти.Он по-прежнему не будет обрабатывать цитирование.

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

Я чувствую себя немного грязно из-за const_cast<>, но программы действительно не должны изменять содержимое строк argv.

Вы можете использовать функцию c_str() std::string для преобразования в char *.Функция strtok разделит строку с помощью разделителя ' '.

Мэтта Пейтрека ЛИБТИНИК имеет модуль с именем argcargv.cpp, который принимает строку и преобразует ее в массив аргументов, принимая во внимание аргументы, заключенные в кавычки.Обратите внимание, что это специфично для Windows, но это довольно просто, поэтому должно быть легко перейти на любую платформу, которую вы хотите.

Если вы сделаете это, также измените его, чтобы принимать в качестве параметров loaction для помещения count и указателя a в массив argv вместо использования externs (просто мой небольшой совет).Мэтту это было не нужно, потому что LIBTINYC был среда выполнения.

В качестве альтернативы вы можете заглянуть в исходный код среды выполнения вашего компилятора (почти все они предоставляют его), чтобы увидеть, что они делают для разбора командной строки, и либо вызвать это напрямую (если это окажется выполнимым), либо позаимствовать идеи из этого фрагмента кода.

Возможно, уже слишком поздно отвечать на этот вопрос, но вы могли бы использовать стандартные функции POSIX шарик или 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);
}

Это подготовило бы 3 параметра: -l (формат длинного списка), -t (сортировка по времени изменения) и каталог /etc перечислить и запустить /bin/ls.Позвонить wordexp() дает вам точно такой же результат, как и вызов /bin/sh -c рекомендованный ранее, но созданный процесс будет иметь родительский процесс, не /bin/sh.

Как оказалось, для этого существует функция, несколько скрытая в настройках программы boost.Функция split_unix() работает с экранированными и заключенными в кавычки командными строками.

#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();
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top