getopt analysiert keine optionalen Argumente für Parameter
Lesezeit: 6 Minuten
hayalci
In C analysiert getopt_long nicht die optionalen Argumente für Befehlszeilenparameter parameter.
Wenn ich das Programm ausführe, wird das optionale Argument nicht erkannt, wie im folgenden Beispiel ausgeführt.
$ ./respond --praise John
Kudos to John
$ ./respond --blame John
You suck !
$ ./respond --blame
You suck !
Hier ist der Testcode.
#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;
}
Ich dokumentiere dies hier mit der Antwort, damit andere Leute nicht mit dem Kopf gegen die Wand schlagen müssen.
– hayalci
27. Juni 2009 um 12:38 Uhr
hayalci
Obwohl in der glibc-Dokumentation oder der getopt-Manpage nicht erwähnt, erfordern optionale Argumente für lange Befehlszeilenparameter das Gleichheitszeichen (=). Das Leerzeichen, das das optionale Argument vom Parameter trennt, funktioniert nicht.
Ein Beispiellauf mit dem Testcode:
$ ./respond --praise John
Kudos to John
$ ./respond --praise=John
Kudos to John
$ ./respond --blame John
You suck !
$ ./respond --blame=John
You suck , John!
Beachten Sie, dass Perls Getopt::Long-Modul NICHT die gleiche Anforderung hat. Boost.Program_options tut es.
– Rob Kennedy
27. Juni 2009 um 15:08 Uhr
Wow, das ist scheiße. Ich bin auf das gleiche Problem mit regulärem getopt() gestoßen und wenn ich einen Optstring “a::” verwende, würde optarg nur gesetzt, wenn Sie NULL Leerzeichen zwischen der Option und dem Argument haben, wie z. B. ‘-afoo’
– SiegeX
20. November 2009 um 23:44 Uhr
Zumindest für meine kurzen Optionen wie -a oder -a=300ich musste ein hinzufügen if (optarg[0] == '=') {memmove(optarg, optarg+1, strlen(optarg));} die abzustreifen =. Sonst hätte ich es immer gehabt =300 in optarg. Oder ich hätte es so nennen müssen: -a300 – was ziemlich hässlich ist, finde ich. Danke trotzdem für deine Antwort, hat mir sehr geholfen!
– mozzbozz
31. Mai 2014 um 17:27 Uhr
In diesem Fall ist es besser, keine optionalen Argumente zu verwenden. Ist das ein Bug oder ein Feature? Jetzt habe ich Lust dazu ./respond --blame=glibc.
– Petersohn
7. März 2015 um 19:11 Uhr
Ich wollte ablehnen, habe dann aber festgestellt, dass Ihre Antwort großartig ist. Es ist nur das Verhalten, das ich ablehnen möchte. Vielen Dank!
– lmat – Wiedereinsetzung von Monica
17. Mai 2019 um 15:58 Uhr
Brian Vandenberg
Die Manpage dokumentiert es sicherlich nicht sehr gut, aber der Quellcode hilft ein wenig.
Kurz gesagt: Sie sollten so etwas wie das Folgende tun (obwohl dies vielleicht etwas übertrieben ist):
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++];
}
/* ... */
Aus den Kommentaren vor _getopt_internal:
…
Wenn getopt findet ein anderes Optionszeichen, gibt es dieses Zeichen zurück, Aktualisierung optind und nextchar damit der nächste Anruf an getopt kann den Scan mit dem folgenden Optionszeichen oder ARGV-Element fortsetzen.
Wenn keine Optionszeichen mehr vorhanden sind, getopt gibt -1 zurück. Dann optind ist der Index in ARGV des ersten ARGV-Elements, das keine Option ist. (Die ARGV-Elemente wurden permutiert, sodass diejenigen, die keine Optionen sind, jetzt an letzter Stelle stehen.) <-- a note from me:
if the 3rd argument to getopt_long starts with a dash, argv will not
be permuted
…
Wenn auf ein Zeichen in OPTSTRING ein Doppelpunkt folgt, bedeutet dies, dass ein Argument gewünscht wird, sodass der folgende Text im selben ARGV-Element oder der Text des folgenden ARGV-Elements zurückgegeben wird optarg. Zwei Doppelpunkte bedeuten eine Option, die ein optionales Argument verlangt; wenn es Text im aktuellen ARGV-Element gibt, wird es zurückgegeben optarg, Andernfalls optarg wird auf Null gesetzt.
…
…wobei man zwischen den Zeilen lesen muss. Folgendes macht was du willst:
#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[optind]
&& '-' != argv[optind][0] ) {
// This is what makes it work; if `optarg` isn't set
// and argv[optind] doesn't look like another option,
// then assume it's our parameter and overtly modify optind
// 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`, `optind`, etc, and they're
// not marked const -- implying they expect and intend you
// to modify them if needed.
tmp_optarg = argv[optind++];
}
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;
}
Das hat wirklich gut funktioniert, danke. Nicht sicher, woher Sie optindex haben; es heißt (extern int) optind für mich.
– panloco
24. Dezember 2017 um 22:21 Uhr
Im zweiten Codebeispiel ist ein Fehler, das sollte es sein optind Anstatt von optindex.
– Sebastian Schrader
14. Januar 2018 um 2:18 Uhr
Sieht so aus, als ob der Code optindex beibehalten sollte. Andernfalls zeigt es immer auf 0. wir müssen optindex für jede Option vorantreiben.
– Howard Shane
16. April 2018 um 19:03 Uhr
Larsewi
Ich bin kürzlich selbst auf dieses Problem gestoßen. Ich kam zu einer ähnlichen Lösung wie die von Brian Vandenberg und Haystack vorgeschlagene. Aber um die Lesbarkeit zu verbessern und Codeduplizierung zu vermeiden, können Sie alles in einem Makro wie dem folgenden zusammenfassen:
Diese Lösung ist getestet und wird – zum Zeitpunkt dieses Schreibens – derzeit in CFEngine verwendet.
Ich bin auch auf das gleiche Problem gestoßen und bin hierher gekommen. Dann wurde mir das klar. Sie haben keinen großen Anwendungsfall von “optional_argument” . Wenn eine Option erforderlich ist, überprüfen Sie die Programmlogik. Wenn eine Option optional ist, müssen Sie nichts tun, da auf der Ebene getopt alle Optionen optional sind, sie sind nicht obligatorisch, sodass es keinen Anwendungsfall für “optional_argument” gibt. Hoffe das hilft.
PS: Für das obige Beispiel denke ich, dass die richtigen Optionen –praise –praise-name “name” –blame –blame-name “name” sind
14112600cookie-checkgetopt analysiert keine optionalen Argumente für Parameteryes
Ich dokumentiere dies hier mit der Antwort, damit andere Leute nicht mit dem Kopf gegen die Wand schlagen müssen.
– hayalci
27. Juni 2009 um 12:38 Uhr