9

execvで実行するコマンドを含むstd::stringがありますが、execv()の2番目のパラメーターで必要な「char * argv []」に変換するための最良の「C++」方法は何ですか?

明確にするために:

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);
4

10 に答える 10

11

ここで非常に非統一的な答え。どうしたの:

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

システムに完全に適切なパーサーが既に存在するのに、なぜコマンドラインパーサーを作成する必要があるのでしょうか。

(注:1つの正当な理由は、実行しようとしている文字列を信頼していないためです。これがすでに正しいことを期待しますが、シェルはその文字列に対して、単純な空白文字スプリッターよりも「多く」のことを行います。注意しないと、セキュリティホールをさらに開けてください。)

于 2009-10-02T21:23:39.267 に答える
5
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"wordexpあなたはそれとはるかに気にするPOSIX関数を考えることができます。

于 2009-10-02T21:27:30.527 に答える
2

c_str()文字列メソッドとstrtok()を組み合わせてスペースで分割すると、exec()とその関連関数に渡す必要のある文字列の配列が得られます。

于 2009-10-02T21:15:28.567 に答える
2

おそらくsplit_winmainBoost.ProgramOptionsから。ほとんどの場合、ブーストは良い選択です。 http://www.boost.org/doc/libs/1_40_0/doc/html/program_options/howto.html#id1396212

Windowsのみに関心がある場合(他のカーネルは通常、Windowsの意味でのコマンドラインについては知りません)、CommandLineToArgvWMSCランタイムと同じ規則を使用するAPI関数を使用できます。

一般に、プラットフォームやシェルの引用スタイルによって異なります。Microsoft Cランタイムは、bashなどとはまったく異なるスタイルを使用します。

于 2009-10-02T21:16:37.280 に答える
1

これは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文字列の内容を変更するべきではありません。

于 2009-10-03T01:24:31.773 に答える
1

OK、私はこれを何度も自分でつまずきました。これはまっすぐな「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;
        }
    }
}
于 2010-04-02T19:39:18.853 に答える
0

std :: stringのc_str()関数を使用して、char*に変換できます。strtok関数は、''区切り文字を使用して文字列を分割します。

于 2009-10-02T21:14:03.193 に答える
0

Matt PeitrekのLIBTINYCには、文字列を取得し、引用符で囲まれた引数を考慮して引数配列に解析するargcargv.cppというモジュールがあります。これはWindows固有であることに注意してください。ただし、非常に単純なので、必要なプラットフォームに簡単に移動できるはずです。

その場合は、externを使用する代わりに、カウントを配置する場所とargv配列へのポインターをパラメーターとして受け取るように変更します(私のちょっとしたアドバイスです)。LIBTINYCランタイムだったので、マットはそれを必要としませんでした。

または、コンパイラのランタイムソース(ほぼすべてが提供)を調べて、コマンドラインを解析するためにコンパイラが何をするかを確認し、それを直接呼び出すか(実行可能であることが判明した場合)、コードのそのビットからアイデアを借りることができます。

于 2009-10-02T21:28:02.270 に答える
0

この質問に答えるには遅すぎるかもしれませんが、標準の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);
}

-l(長いリスト形式)、-t(変更時間でソート)、リストするディレクトリの3つのパラメータを準備/etcし、を実行します/bin/ls。呼び出しwordexp()は、以前に推奨された呼び出しとまったく同じ結果をもたらし/bin/sh -cますが、spawanedプロセスには親プロセスがありません/bin/sh

于 2016-11-16T21:14:05.200 に答える
0

結局のところ、このためのブーストプログラムオプションには関数がいくらか隠されています。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();
于 2018-10-31T18:02:41.833 に答える