2009-10-02 11 views
7

私はstd :: stringにexecvで実行されるコマンドが含まれていますが、これは "char * argv []"に変換するのに最適な "C++"方法です。 execv()の2番目のパラメータ?明確にするために文字列をC++でargvに変換する

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

答えて

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"のように引用符を使用commansでは動作しません。 POSIX関数wordexpを気にすることができます。

+0

引用符付きのコマンドは、execvが配列をとる理由です。つまり、引用規則を決定するのを避けるためです。質問者が彼が望む規則を引用していない限り、質問に答えることはできません。彼が正しく指定した時点で、答えは "あなたが指定した文法のパーサーを生成します"; –

+0

@onebyone trueおそらくプログラム中のある時期に、彼はすでに議論をしていました。私たちは分かりません。しかし、文字列だけで、ロギングや監査のような目的で分割する必要がある場合は、 'wordexp'などについて知っておくと便利です。一般に' sh'にプッシュすることは良いアイデア。 –

0

あなたはSTDのc_str()関数を使用することができます::文字列をcharに変換するために、*。 strtok関数は、区切り文字を使用して文字列を分割します。

+0

'execv'に渡す前に、文字列をスペースで分割する必要があります。引数には配列内の別々の項目として渡す必要があります。 –

+0

そうです。答えを書いたときのコード例はありませんでした。 –

2

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

+1

単一の引数にスペースを含めるためにsupoprtの引用符を使用しない場合は、これは問題ありません。 –

+0

ええ、ええ、あなたは注意する必要があります。引用は全く新しいワームです。 –

1

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

は、Windowsにのみ関心がある場合には(他のカーネルは、一般的に、Windowsの意味でのコマンドラインを知らない)、あなたはMS Cランタイムと同じ規則を使用してAPI関数CommandLineToArgvWを使用することができます。

一般に、プラットフォームおよび/またはシェルのクォートスタイルによって異なります。 Microsoft Cランタイムは、例えば、 bash!

10

ここでは非常に非ユニークな答えがあります。何が問題なのですか?

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

システムに完全に優れたものがある場合、コマンドラインパーサーを書くのはなぜでしょうか?

(注:あなたが実行しようとしている文字列を信用していないために1つの理由があります。これはすでに正しいと考えられますが、シェルは、スプリッタは意志とあなたが注意されていない場合ので、より多くのセキュリティホールを開きます。)もちろん

+0

システムに1つもない場合はどうなりますか? –

+0

execve()システムコールを持ち、シェルを持たないシステム?そのような獣は聞いたことがありません。 –

+0

上記の2行だけのテストプログラムを実行し、cmd = "echo hello world"を実行すると、execl呼び出しが成功せず、戻ります。最初の引数を "sh"から "/ bin/sh"に変更すると、エラー "-c:echo hello world:そのようなファイルやディレクトリはありません" – aaronstacy

0

Matt PeitrekのLIBTINYCにはargcargv.cppというモジュールがあり、これは文字列を受け取り、引用された引数を考慮して引数配列に解析します。それはWindows固有のものであることに注意してください。しかし、それはかなりシンプルなので、どんなプラットフォームにでも簡単に移動できます。

もしあなたがそうするなら、externs(ちょっと私のちょっとしたアドバイス)を使わずに、引数とポインタをargv配列に入れるためにloactionをパラメータとして使うように変更してください。 LIBTINYC は実行時にだったので、Mattはそれを必要としませんでした。

また、コンパイラのランタイムソース(ほぼすべて提供しています)を見て、コマンドラインを解析して直接実行することができます(実行可能と判明した場合)か、そのビットからアイデアを借りてくださいコードの

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

+0

'v1'が大きくなると、' v2'のすべてのポインタを無効にすることができます。 –

+0

@BenJacksonありがとう、リストに変更。 –

1

OK、私は十分な時間を自分自身につまずいてきました。これはまっすぐな "C"なので、CまたはC++にプラグインすることができます。これは、一重引用符と二重引用符を別々に扱います。呼び出し側はargv [0](NULLでない場合)とargvの割り当てを解除します。 -l(ロングリスト形式)、-t:それは3つのパラメータを準備します

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

#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; 
     } 
    } 
}
0

が、この質問に答えるために遅すぎるかもしれないが、あなたはスタンダールPOSIX機能globまたはwordexpを使用することができます(変更時刻で並べ替える)とディレクトリ/etcを表示し、/bin/lsを実行します。電話wordexp()は、前に/bin/sh -cを呼び出すと同じ結果を返しますが、親プロセスは/bin/shではありません。

関連する問題