преобразовать строку в argv на c ++
Вопрос
У меня есть 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();