Gibt es in C eine Möglichkeit, ein Stück Text zu analysieren und Werte für argv und argc zu erhalten, als ob der Text auf der Befehlszeile an eine Anwendung übergeben worden wäre?
Dies muss nicht unter Windows funktionieren, sondern nur unter Linux – es ist mir auch egal, Argumente zu zitieren.
Für welche Plattform? Wie Befehlszeilen in argc/argv geparst werden, ist beispielsweise zwischen Windows- und UNIX-basierten Systemen ziemlich unterschiedlich. Unter UNIX transformiert die Shell die Befehlszeile normalerweise erheblich, einschließlich Globbing (Dateimustererweiterung) sowie Variablenersetzung. Unter Windows wird die Dateimustererweiterung nicht von der Shell durchgeführt (es sei denn, Sie verwenden natürlich etwas wie Cygwin oder MKS Toolkit).
– Laurence Gonsalves
10. November 2009 um 9:18 Uhr
Wenn Sie nicht einmal mit Anführungszeichen umgehen müssen, würde ich wirklich vorschlagen, Ihre eigene Funktion zu codieren, anstatt eine Bibliothek eines Drittanbieters nur für diese Aufgabe einzuführen.
– Remo.D
10. November 2009 um 10:15 Uhr
Hast du es mit getopt() versucht? (Mann 3 getopt). Sie können die meisten UNIX/Linux-Standard-Tool-Quellen für Beispiele sehen, eine RIESIGE Anzahl davon. Sogar die Manpage (zumindest Linux) enthält ein anständiges Beispiel. Es gibt auch eine Reihe von Wrappern (Sie sehen hier Empfehlungen), aber getopt() scheint der einzige zu sein, der für JEDE UNIX-Plattform verfügbar ist (eigentlich scheint es Teil des POSIX-Standards zu sein).
– Roman Nikitschenko
10. November 2009 um 11:26 Uhr
Wenn Sie immer noch interessiert sind und industrielle Stärke von Grund auf neu wollen, in einem kleinen Codepaket. Suchen Sie auf dieser Seite nach nargv Bei weitem die beste Lösung, die ich hier aus reinem C-Code gesehen habe. Bitte stimmen Sie diese Antwort ab! Damit andere es finden können.
– user735796
9. April 2012 um 10:32 Uhr
@ user735796 Ich habe gesucht nargv und dein Kommentar ist der einzige Hit. Also googelte ich: github.com/hypersoft/nargv … Einige Kommentare. Dies verwendet C99 und funktioniert daher nicht mit dem Microsoft C-Compiler. Es ist auch eine Idee, Komponententests mit einer Reihe von Testfällen zu haben, die jede Art von Szenario für den Parser überprüfen, um zu überprüfen, ob es wie erwartet funktioniert.
– Joakim
1. März 2015 um 11:14 Uhr
Ich bin überrascht, dass niemand die einfachste Antwort mit der Standard-POSIX-Funktionalität gegeben hat:
Das kann mehr bewirken, als Sie wollen. ZB führt es Erweiterungen von Shell-Wörtern durch, einschließlich der Ersetzung von Umgebungsvariablen, z. B. der Ersetzung $PATH mit dem aktuellen Pfad.
– Craig McQueen
3. April 2013 um 0:57 Uhr
Ich denke, es hängt davon ab, was Sie mit parse in argc/argv meinen; Sicherlich beinhaltet dies einiges von dem, was die Shell tut (Verarbeitung von Zitaten), aber die Erweiterung von Variablen und andere Dinge sind fragwürdiger. Übrigens wordexp hat eine Option zum Deaktivieren der Befehlserweiterung.
– R.. GitHub HÖR AUF, EIS ZU HELFEN
3. April 2013 um 1:49 Uhr
Wenn du meinst WRDE_NOCMDdas scheint die Expansion nicht zu verhindern $PATHnoch erweitern * zu den Namen der Dateien im aktuellen Verzeichnis.
– Craig McQueen
3. April 2013 um 1:57 Uhr
Ich habe nicht gesagt, dass es die Variablenerweiterung verhindert, nur dass eine andere Sache, die Sie vielleicht deaktivieren möchten, die Befehlserweiterung, deaktiviert werden kann.
– R.. GitHub HÖR AUF, EIS ZU HELFEN
3. April 2013 um 2:21 Uhr
Das ist genau das, wonach ich gesucht habe und scheint sehr gut zu funktionieren. Ich brauchte es, um einen benutzerdefinierten Befehl an zu übergeben posix_spawn, ohne zu wissen, ob es weitere Argumente geben würde. Ein kurzes Codebeispiel würde diese Antwort jedoch so viel besser machen. Ja, sogar jetzt, mehr als sieben Jahre später. 🙂
– Domson
19. Februar 2018 um 20:43 Uhr
Remo.D
Wenn die Glib-Lösung für Ihren Fall zu viel des Guten ist, können Sie erwägen, selbst eine zu programmieren.
Dann kannst du:
scannen Sie die Zeichenfolge und zählen Sie, wie viele Argumente es gibt (und Sie erhalten Ihr argc)
Weisen Sie ein Array von char * zu (für Ihr argv)
Scannen Sie die Zeichenfolge erneut, weisen Sie die Zeiger im zugewiesenen Array zu und ersetzen Sie Leerzeichen durch ‘\0’ (wenn Sie die Zeichenfolge mit den Argumenten nicht ändern können, sollten Sie sie duplizieren).
Vergessen Sie nicht, das freizugeben, was Sie zugewiesen haben!
Das folgende Diagramm sollte (hoffentlich) verdeutlichen:
char **parseargs(char *arguments, int *argc);
void freeparsedargs(char **argv);
Sie müssen zusätzliche Überlegungen anstellen, um freeparsedargs() sicher zu implementieren.
Wenn Ihre Zeichenfolge sehr lang ist und Sie nicht zweimal scannen möchten, können Sie Alternativen in Betracht ziehen, z. B. mehr Elemente für die argv-Arrays zuweisen (und bei Bedarf neu zuweisen).
BEARBEITEN: Vorgeschlagene Lösung (behandelt das zitierte Argument nicht).
#include <stdio.h>
static int setargs(char *args, char **argv)
{
int count = 0;
while (isspace(*args)) ++args;
while (*args) {
if (argv) argv[count] = args;
while (*args && !isspace(*args)) ++args;
if (argv && *args) *args++ = '\0';
while (isspace(*args)) ++args;
count++;
}
return count;
}
char **parsedargs(char *args, int *argc)
{
char **argv = NULL;
int argn = 0;
if (args && *args
&& (args = strdup(args))
&& (argn = setargs(args,NULL))
&& (argv = malloc((argn+1) * sizeof(char *)))) {
*argv++ = args;
argn = setargs(args,argv);
}
if (args && !argv) free(args);
*argc = argn;
return argv;
}
void freeparsedargs(char **argv)
{
if (argv) {
free(argv[-1]);
free(argv-1);
}
}
int main(int argc, char *argv[])
{
int i;
char **av;
int ac;
char *as = NULL;
if (argc > 1) as = argv[1];
av = parsedargs(as,&ac);
printf("== %d\n",ac);
for (i = 0; i < ac; i++)
printf("[%s]\n",av[i]);
freeparsedargs(av);
exit(0);
}
weil getopt einen anderen Job macht. Es nimmt eine Reihe von Argumenten und sucht darin nach Optionen. Bei dieser Frage geht es darum, eine Zeichenfolge von “Argumenten” in ein Array von char * aufzuteilen, was getopt nicht kann
– Remo.D
10. November 2009 um 20:28 Uhr
Wenn Sie die Eingabezeichenfolge so umwandeln, können Sie keine Zeichenfolgenverkettung mit Anführungszeichen wie “this” oder “this” durchführen. Siehe meine Antwort für eine voll funktionsfähige Lösung.
– user735796
9. April 2012 um 18:32 Uhr
(Spitze im Voraus) Beachten Sie, dass eine kleine Sache fehlt, um mit dem Standard konform zu sein argc/argv layout: Der Eintrag hinter dem letzten gültigen in argv ist immer eingestellt NULL ("foo bar": argv[0] -> "foo", argv[1] -> "bar", argv[2] -> NULL).
– Max Truxa
20. Oktober 2014 um 13:11 Uhr
ssteevvee
Hier mein Beitrag. Es ist schön und kurz, aber Dinge, auf die Sie achten sollten, sind:
Die Verwendung von strtok ändert die ursprüngliche “commandLine”-Zeichenfolge und ersetzt die Leerzeichen durch \0-Trennzeichen am Ende der Zeichenfolge
argv[] zeigt am Ende auf “commandLine”, also ändern Sie es nicht, bis Sie mit argv fertig sind[].
Sie können jetzt argc und argv verwenden oder sie an andere Funktionen übergeben, die wie “foo(int argc, char **argv)” deklariert sind.
Danke, das hat etwas Zeit gespart. An alle anderen, die dies verwenden: “char* p1” (obwohl Ihr Compiler Ihnen gesagt hätte =])
– jrr
30. September 2013 um 21:02 Uhr
Berücksichtigt dies maskierte Argumente wie “some/long path/to file.txt”?
– Gier
17. März um 15:19 Uhr
Nein, Sie müssten danach selbst nach Angeboten suchen und diese bearbeiten. Wenn Ihr Code auf einem “echten” Betriebssystem ausgeführt wird, würde ich empfehlen, zu sehen, was es bietet. Zum Beispiel die Glib, wie in einer anderen Lösung vorgeschlagen, die Ihnen diese Art von Funktionen bieten sollte.
Wenn Sie nicht einmal daran interessiert sind, zu zitieren, ist dies möglicherweise übertrieben. Alles, was Sie tun müssen, ist die Tokenisierung, indem Sie Leerzeichen als Tokenzeichen verwenden. Das Schreiben einer einfachen Routine sollte wirklich nicht lange dauern.
Wenn Sie beim Speicher nicht sehr geizig sind, sollte es einfach sein, dies in einem Durchgang ohne Neuzuweisungen zu tun. Nehmen Sie einfach an, dass im schlimmsten Fall jedes zweite Zeichen ein Leerzeichen ist, und nehmen Sie somit eine Zeichenfolge von an n Zeichen enthält höchstens (n + 1) / 2 Argumente, und (natürlich) höchstens n Bytes Argumenttext (ohne Abschlusszeichen).
Joakim
Hier ist eine Lösung für Windows und Unix (getestet auf Linux, OSX und Windows). Getestet mit Valgrind und Dr. Speicher.
Beachten Sie, dass bei der Windows-Lösung der größte Teil des Codes zwischen konvertiert wird char ** und wchar_t ** mit der schönen Win32-API, da gibt es keine CommandLineToArgvA verfügbar (ANSI-Version).
#ifdef _WIN32
#include <windows.h>
#else
#include <wordexp.h>
#endif
char **split_commandline(const char *cmdline, int *argc)
{
int i;
char **argv = NULL;
assert(argc);
if (!cmdline)
{
return NULL;
}
// Posix.
#ifndef _WIN32
{
wordexp_t p;
// Note! This expands shell variables.
if (wordexp(cmdline, &p, 0))
{
return NULL;
}
*argc = p.we_wordc;
if (!(argv = calloc(*argc, sizeof(char *))))
{
goto fail;
}
for (i = 0; i < p.we_wordc; i++)
{
if (!(argv[i] = strdup(p.we_wordv[i])))
{
goto fail;
}
}
wordfree(&p);
return argv;
fail:
wordfree(&p);
}
#else // WIN32
{
wchar_t **wargs = NULL;
size_t needed = 0;
wchar_t *cmdlinew = NULL;
size_t len = strlen(cmdline) + 1;
if (!(cmdlinew = calloc(len, sizeof(wchar_t))))
goto fail;
if (!MultiByteToWideChar(CP_ACP, 0, cmdline, -1, cmdlinew, len))
goto fail;
if (!(wargs = CommandLineToArgvW(cmdlinew, argc)))
goto fail;
if (!(argv = calloc(*argc, sizeof(char *))))
goto fail;
// Convert from wchar_t * to ANSI char *
for (i = 0; i < *argc; i++)
{
// Get the size needed for the target buffer.
// CP_ACP = Ansi Codepage.
needed = WideCharToMultiByte(CP_ACP, 0, wargs[i], -1,
NULL, 0, NULL, NULL);
if (!(argv[i] = malloc(needed)))
goto fail;
// Do the conversion.
needed = WideCharToMultiByte(CP_ACP, 0, wargs[i], -1,
argv[i], needed, NULL, NULL);
}
if (wargs) LocalFree(wargs);
if (cmdlinew) free(cmdlinew);
return argv;
fail:
if (wargs) LocalFree(wargs);
if (cmdlinew) free(cmdlinew);
}
#endif // WIN32
if (argv)
{
for (i = 0; i < *argc; i++)
{
if (argv[i])
{
free(argv[i]);
}
}
free(argv);
}
return NULL;
}
Und ich
Ich habe dies gerade für ein eingebettetes Projekt in einfachem C getan, wo ich eine kleine CLI habe, die die Eingabe der seriellen Schnittstelle analysiert und eine begrenzte Menge von Befehlen mit den Parametern ausführt.
Dies ist wahrscheinlich nicht das Ordentlichste, aber so klein und effizient, wie ich es bekommen könnte:
int main(void) {
char **myargs;
int argc;
int numargs = makeargs("Hello world, this is a test", &argc, &myargs);
while (numargs) {
printf("%s\r\n", myargs[argc - numargs--]);
};
return (EXIT_SUCCESS);
}
Michael Burr
Matt Peitreks LIBTINYC hat ein Modul namens argcargv.cpp, das eine Zeichenfolge nimmt und sie unter Berücksichtigung von Argumenten in Anführungszeichen in das Argument-Array ausgibt. Beachten Sie, dass es Windows-spezifisch ist, aber es ist ziemlich einfach, also sollte es einfach sein, auf jede gewünschte Plattform zu wechseln.
Mit dem kleinen Problem, dass C++ und nicht C ist 🙂
– Remo.D
10. November 2009 um 9:14 Uhr
Benennen Sie die Datei in argcargv.c um und es ist C. Buchstäblich.
– Michael Burr
10. November 2009 um 15:10 Uhr
Die Bibliothek von Herrn Peitrek scheint sehr schwach zu sein, verglichen mit den tatsächlichen Regeln von Microsoft zur Trennung einer Befehlszeile in argc/argv (siehe msdn.microsoft.com/en-us/library/17w5ykft.aspx für ihre Regeln). Natürlich kein Problem, wenn das nicht nötig ist, aber die Leute sollten sicher sein, dass sie bekommen, was sie brauchen!
– Steve Valliere
23. Mai 2013 um 15:07 Uhr
Außerdem ist es völlig unnötig, da Microsoft Ihnen nicht nur die Spezifikation gibt, wie sie die Befehlszeile analysieren, sondern auch eine API dafür bereitstellt: CommandLineToArgvW
Für welche Plattform? Wie Befehlszeilen in argc/argv geparst werden, ist beispielsweise zwischen Windows- und UNIX-basierten Systemen ziemlich unterschiedlich. Unter UNIX transformiert die Shell die Befehlszeile normalerweise erheblich, einschließlich Globbing (Dateimustererweiterung) sowie Variablenersetzung. Unter Windows wird die Dateimustererweiterung nicht von der Shell durchgeführt (es sei denn, Sie verwenden natürlich etwas wie Cygwin oder MKS Toolkit).
– Laurence Gonsalves
10. November 2009 um 9:18 Uhr
Wenn Sie nicht einmal mit Anführungszeichen umgehen müssen, würde ich wirklich vorschlagen, Ihre eigene Funktion zu codieren, anstatt eine Bibliothek eines Drittanbieters nur für diese Aufgabe einzuführen.
– Remo.D
10. November 2009 um 10:15 Uhr
Hast du es mit getopt() versucht? (Mann 3 getopt). Sie können die meisten UNIX/Linux-Standard-Tool-Quellen für Beispiele sehen, eine RIESIGE Anzahl davon. Sogar die Manpage (zumindest Linux) enthält ein anständiges Beispiel. Es gibt auch eine Reihe von Wrappern (Sie sehen hier Empfehlungen), aber getopt() scheint der einzige zu sein, der für JEDE UNIX-Plattform verfügbar ist (eigentlich scheint es Teil des POSIX-Standards zu sein).
– Roman Nikitschenko
10. November 2009 um 11:26 Uhr
Wenn Sie immer noch interessiert sind und industrielle Stärke von Grund auf neu wollen, in einem kleinen Codepaket. Suchen Sie auf dieser Seite nach
nargv
Bei weitem die beste Lösung, die ich hier aus reinem C-Code gesehen habe. Bitte stimmen Sie diese Antwort ab! Damit andere es finden können.– user735796
9. April 2012 um 10:32 Uhr
@ user735796 Ich habe gesucht
nargv
und dein Kommentar ist der einzige Hit. Also googelte ich: github.com/hypersoft/nargv … Einige Kommentare. Dies verwendet C99 und funktioniert daher nicht mit dem Microsoft C-Compiler. Es ist auch eine Idee, Komponententests mit einer Reihe von Testfällen zu haben, die jede Art von Szenario für den Parser überprüfen, um zu überprüfen, ob es wie erwartet funktioniert.– Joakim
1. März 2015 um 11:14 Uhr