преобразовать строку в argv в c ++ - PullRequest
8 голосов
/ 03 октября 2009

У меня есть 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);

Ответы [ 10 ]

10 голосов
/ 03 октября 2009

Очень не-юниксы ответы здесь. Что не так с:

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

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

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

5 голосов
/ 03 октября 2009
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, которая заботится об этом и о многом другом.

2 голосов
/ 03 октября 2009

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

1 голос
/ 02 апреля 2010

Хорошо, я сам спотыкался об этом достаточно раз. Это прямое «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;
        }
    }
}
1 голос
/ 03 октября 2009

Это вариант ответа 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.

1 голос
/ 03 октября 2009

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

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

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

0 голосов
/ 31 октября 2018

Как выяснилось, для этого есть несколько скрытые функции в опциях программы 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();
0 голосов
/ 17 ноября 2016

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

0 голосов
/ 03 октября 2009

Matt Peitrek's LIBTINYC имеет модуль argcargv.cpp, который принимает строку и анализирует ее в массиве аргументов с учетом аргументов в кавычках. Обратите внимание, что это зависит от Windows, но это довольно просто, поэтому должно быть легко перейти на любую платформу, которую вы хотите.

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

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

0 голосов
/ 03 октября 2009

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

...