سؤال

في لغة C، لا يقوم getopt_long بتحليل الوسائط الاختيارية لمعلمات معلمات سطر الأوامر.

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

$ ./respond --praise John
Kudos to John
$ ./respond --blame John
You suck !
$ ./respond --blame
You suck !

هنا هو رمز الاختبار.

#include <stdio.h>
#include <getopt.h>

int main(int argc, char ** argv )
{
    int getopt_ret, option_index;
    static struct option long_options[] = {
               {"praise",  required_argument, 0, 'p'},
               {"blame",  optional_argument, 0, 'b'},
               {0, 0, 0, 0}       };
    while (1) {
        getopt_ret = getopt_long( argc, argv, "p:b::",
                                  long_options,  &option_index);
        if (getopt_ret == -1) break;

        switch(getopt_ret)
        {
            case 0: break;
            case 'p':
                printf("Kudos to %s\n", optarg); break;
            case 'b':
                printf("You suck ");
                if (optarg)
                    printf (", %s!\n", optarg);
                else
                    printf ("!\n", optarg);
                break;
            case '?':
                printf("Unknown option\n"); break;
        }
    } 
    return 0;
}
هل كانت مفيدة؟

المحلول

وعلى الرغم من عدم ذكرها في وثائق سي العمومية أو صفحة رجل getopt، والحجج اختياري لمعلمات سطر الأوامر نمط طويل تتطلب "علامة يساوي '(=). مساحة تفصل بين سيطة اختيارية من المعلمة لا تعمل.

ومثال تشغيل مع رمز اختبار:

<اقتباس فقرة>
$ ./respond --praise John
Kudos to John
$ ./respond --praise=John
Kudos to John
$ ./respond --blame John
You suck !
$ ./respond --blame=John
You suck , John!

نصائح أخرى

من المؤكد أن صفحة الدليل لا توثق ذلك بشكل جيد، ولكن الكود المصدري يساعد قليلاً.

باختصار:من المفترض أن تفعل شيئًا مثل ما يلي (على الرغم من أن هذا قد يكون مبالغًا فيه قليلاً):

if(   !optarg
   && optind < argc // make sure optind is valid
   && NULL != argv[optind] // make sure it's not a null string
   && '\0' != argv[optind][0] // ... or an empty string
   && '-' != argv[optind][0] // ... or another option
  ) {
  // update optind so the next getopt_long invocation skips argv[optind]
  my_optarg = argv[optind++];
}
/* ... */

من بين التعليقات السابقة _getopt_internal:

...

لو getopt يعثر على حرف خيار آخر، ويقوم بإرجاع هذا الحرف، تحديث optind و nextchar حتى أن المكالمة التالية ل getopt يمكن استئناف الفحص باستخدام حرف الخيار التالي أو عنصر ArgV.

إذا لم يكن هناك المزيد من أحرف الخيارات، getopt عوائد -1.ثم optind هو الفهرس في ArgV من العناصر Argv الأولى التي ليست خيارًا.(تم توريث عناصر Argv بحيث تأتي تلك التي لا تكون خيارات الآن.) <-- a note from me: if the 3rd argument to getopt_long starts with a dash, argv will not be permuted

...

إذا تمت متابعة char في OptString بواسطة قولون ، فهذا يعني أنه يريد arg ، وبالتالي يتم إرجاع النص التالي في نفس عنصر argv ، أو نص argv العناصر التالية ، optarg.يعني اثنان من كولون خيار يريد arg اختياري ؛إذا كان هناك نص في عنصر ArgV الحالي ، يتم إرجاعه optarg, خلاف ذلك optarg تم ضبطه على الصفر.

...

...على الرغم من أنه عليك القيام ببعض القراءة بين السطور.ما يلي يفعل ما تريد:

#include <stdio.h>
#include <getopt.h>

int main(int argc, char* argv[] ) {
  int getopt_ret;
  int option_index;
  static struct option long_options[] = {
      {"praise",  required_argument, 0, 'p'}
    , {"blame",  optional_argument, 0, 'b'}
    , {0, 0, 0, 0}
  };

  while( -1 != ( getopt_ret = getopt_long(  argc
                                          , argv
                                          , "p:b::"
                                          , long_options
                                          , &option_index) ) ) {
    const char *tmp_optarg = optarg;
    switch( getopt_ret ) {
      case 0: break;
      case 1:
        // handle non-option arguments here if you put a `-`
        // at the beginning of getopt_long's 3rd argument
        break;
      case 'p':
        printf("Kudos to %s\n", optarg); break;
      case 'b':
        if(   !optarg
           && NULL != argv[optindex]
           && '-' != argv[optindex][0] ) {
          // This is what makes it work; if `optarg` isn't set
          // and argv[optindex] doesn't look like another option,
          // then assume it's our parameter and overtly modify optindex
          // to compensate.
          //
          // I'm not terribly fond of how this is done in the getopt
          // API, but if you look at the man page it documents the
          // existence of `optarg`, `optindex`, etc, and they're
          // not marked const -- implying they expect and intend you
          // to modify them if needed.
          tmp_optarg = argv[optindex++];
        }
        printf( "You suck" );
        if (tmp_optarg) {
          printf (", %s!\n", tmp_optarg);
        } else {
          printf ("!\n");
        }
        break;
      case '?':
        printf("Unknown option\n");
        break;
      default:
        printf( "Unknown: getopt_ret == %d\n", getopt_ret );
        break;
    }
  }
  return 0;
}

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

ملاحظة: للمثال المذكور أعلاه وأعتقد أن الخيارات الصحيحة --praise --praise اسم "اسم" --blame --blame اسم "اسم"

إذا كنت أكتب الحجة القادمة إلى المعلمة دون حرف مسافة لا يساوي يعمل أيضا. على سبيل المثال:

$ ./respond --blameJohn
You suck John!
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top