سؤال

لدي سلسلة STD :: سلسلة تحتوي على أمر يتم تنفيذه مع 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];

بالطبع، هذا لن يعمل مع Commans التي تستخدم اقتباس مثل rm "a file.mp3". وبعد يمكنك التفكير في وظيفة بوسيكس wordexp الذي يهتم ذلك وأكثر من ذلك بكثير.

نصائح أخرى

إجابات غير مدهونة للغاية هنا. ما المشكلة في:

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

لماذا تهتم كتابة محلل سطر الأوامر عندما يكون هناك واحد جيد بالفعل في النظام؟

(ملاحظة: أحد الأسباب الجيدة هو لأنك لا تثق في السلسلة التي أنت على وشك التنفيذ. يأمل المرء أن يكون هذا صحيحا بالفعل، لكن القذيفة ستفعل "أكثر" مع تلك السلسلة أكثر من الخائن البياضات السذاجة وبالتالي افتح المزيد من الثقوب الأمنية إذا لم تكن حذرا.)

يجب أن تحصل عليك مزيج من طريقة سلسلة C_STR () و Strtok () لتقسيمها بواسطة مسافات من خلال مجموعة من السلاسل التي تحتاج إلى تمريرها إلى Exec () ووظائفها ذات الصلة.

ربما split_winmain من دفعة.ProgramOptions. يعد Boost خيارا جيدا في معظم الحالات.http://www.boost.org/doc/libs/1_40_0/doc/html/program_options/howto.htmlbls1396212.

إذا كنت مهتما فقط في Windows (لا تعرف حبات أخرى عموما عن خطوط الأوامر في المعنى بالنظام)، فيمكنك استخدام وظيفة API CommandLineToArgvW التي تستخدم نفس الاتفاقيات مثل وقت تشغيل MS C.

بشكل عام، يعتمد ذلك على نمط نقلا عن النظام الأساسي و / أو قذيفة. يستخدم وقت التشغيل Microsoft C أسلوبا مختلفا تماما من مثل باش!

حسنا، لقد تعثرت على هذا نفسي أوقاتا كافية. هذا مستقيم "C"، لذلك يمكن توصيله إما C أو C ++. يعامل سلاسل اقتباس واحدة ومزدوجة بشكل مختلف. المتصل هو المسؤول عن إلغاء تخصيص Argv [0] (إن لم يكن null) والأرجف.

#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_ast <>، ولكن لا ينبغي أن تعدل البرامج حقا محتويات سلاسل Argv.

يمكنك استخدام وظيفة C_STR () STD :: سلسلة للتحويل إلى Char *. سيقوم وظيفة Strtok بتقسيم السلسلة باستخدام "محدد".

مات بيتريك libtinyc. يحتوي على وحدة نمطية تسمى argcargv.cpp التي تأخذ سلسلة وتؤديها إلى صفيف الوسيطة اتخاذ حجج مقتبسة في الاعتبار. لاحظ أنه خاص بنظام التشغيل Windows، لكنه بسيط جدا، لذلك يجب أن يكون من السهل الانتقال إلى أي منصة تريدها.

إذا قمت بذلك، فقم أيضا بتغييرها لاتخاذ كمعلمات لعنة وضع العد ومؤشر إلى صفيف Argv بدلا من استخدام externs (فقط قليلا من نصيحة النصيحة). مات لم يحتاج ذلك لأن libtinyc كنت وقت التشغيل.

بدلا من ذلك، يمكنك البحث في مصدر وقت تشغيل برنامج التحويل البرمجي الخاص بك (جميعها تقريبا توفيرها) لمعرفة ما يفعلونه لتحليل Commandline وإما يدعو ذلك مباشرة (إذا اتضح أن تكون قابلة للتطبيق) أو استعارة الأفكار من هذا القليل من التعليمات البرمجية.

قد يكون بعد فوات الأوان للإجابة على هذا السؤال ولكن يمكنك استخدام وظائف بوسيكس ستاندارت غلوب أو 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.

كما اتضح أن وظيفة موجودة إلى حد ما في خيارات برنامج التعزيز لهذا البرنامج. تعمل الدالة SPLIPT_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